Initial import of elftoolchain r2974.

Obtained from:	elftoolchain.org
This commit is contained in:
Kai Wang 2014-01-15 08:43:20 +00:00
commit 5265ace0e4
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/vendor/elftoolchain/dist/; revision=260684
svn path=/vendor/elftoolchain/elftoolchain-r2974/; revision=260685; tag=vendor/elftoolchain/elftoolchain-r2974
1811 changed files with 352024 additions and 0 deletions

22
GNUmakefile Normal file
View File

@ -0,0 +1,22 @@
# -*- mode: makefile; -*-
#
# Issue a useful error message if a user tries to build the project
# using GNU make.
all:
$(error ERROR: This source tree needs to be built with BSD 'make'.)
# Some GNU/Linux distributions offer pre-built packages of BSD 'make':
#
# - On Debian-derived distributions, the "pmake" package provides an
# older version of BSD 'make' that should suffice.
# - On Fedora, the 'bmake' package seems appropriate (untested).
#
# Portable source code for NetBSD 'make' may be found at:
# http://www.crufty.net/help/sjg/bmake.html
#
#
# Please also read the file "INSTALL" for additional information about
# building the project from source.
#
# $Id: GNUmakefile 2568 2012-09-04 12:13:21Z jkoshy $

283
INSTALL Normal file
View File

@ -0,0 +1,283 @@
Installation Instructions
=========================
This file contains instructions on building and installing the
libraries and utilities in the elftoolchain project's sources.
Supported Operating Systems
---------------------------
The source tree is currently built and tested on the following
operating systems.
================= ======== =======================
Operating System Version Supported Architectures
----------------- -------- -----------------------
`DragonFly BSD`_ 2.10.1 i386
FreeBSD_ v8.2 amd64 & i386
Minix_ 3.0.2 i386
NetBSD_ v5.0.2 i386
OpenBSD_ v5.0 i386
Ubuntu_ GNU/Linux 10.04LTS i386 & x86_64
================= ======== =======================
.. _DragonFly BSD: http://www.dragonflybsd.org/
.. _FreeBSD: http://www.freebsd.org/
.. _Minix: http://www.minix3.org/
.. _NetBSD: http://www.netbsd.org/
.. _OpenBSD: http://www.openbsd.org/
.. _Ubuntu: http://www.ubuntu.com/
Building the Source Tree
========================
The core libraries and utilities that make up the software release are
always built by default. Builds of the project's test suites (in the
``test/`` subdirectory), and of additional documentation (in the
directory ``documentation/``) are optional and will only be attempted
if these directories are present.
Prerequisites
-------------
:DragonFly BSD 2.10.1:
- The core libraries and utilities should build out of the box on
a stock install of DragonFly BSD.
- To build and run the test suite:
#. The current release of the `Test Execution Toolkit`_ needs to
be downloaded and unpacked into the ``test/tet/`` directory.
#. The ``py26-yaml`` package needs to be installed::
% sudo pkgin install py26-yaml
- Building additional documentation is not currently supported
under DragonFly BSD.
:FreeBSD 8.2:
- The core libraries and utilities should build out of the box on
a stock install of FreeBSD.
- To build and run the test suite:
#. The current release of the `Test Execution Toolkit`_ needs to
be downloaded and unpacked into the ``test/tet/`` directory.
#. The ``py-yaml`` package needs to be installed::
% sudo pkg_add -r py-yaml
- To build additional documentation, the ``latex-pgf`` package is
needed::
% sudo pkg_add -r latex-pgf
:Minix 3.2.0:
- The following packages are pre-requisites for building the
sources on Minix 3.2.0:
=================== =====================================
**Package** **Description**
=================== =====================================
``gcc44`` The GNU C compiler.
=================== =====================================
The following command line may be used to install the necessary
pre-requisites::
# pkgin install gcc44
- The test suites cannot currently be built under Minix.
- Building additional documentation is not currently supported
under Minix.
:OpenBSD 5.0:
- The following packages are pre-requisites for building the
sources on OpenBSD 5.0:
=================== =====================================
**Package** **Description**
=================== =====================================
``libarchive`` An archive access library.
=================== =====================================
The following command line may be used to install the necessary
pre-requisites::
# pkg_add libarchive-2.8.4p0
- The test suites cannot currently be built under OpenBSD.
- Building additional documentation is not currently supported
under OpenBSD.
:NetBSD 5.0.2:
- The core libraries and utilities should build out of the box
on a stock install of NetBSD.
- To build and run the test suite:
#. The current release of the `Test Execution Toolkit`_, needs
to be downloaded and unpacked into the ``test/tet/``
directory.
#. The following additional package needs to be installed, as
listed in the example command line below ::
% sudo pkg_add py26-yaml
- Building additional documentation is not currently supported
under NetBSD.
:Ubuntu GNU/Linux 10.04:
- The following packages are pre-requisites for building the
sources on Ubuntu GNU/Linux 10.04:
=================== =====================================
**Package** **Description**
=================== =====================================
``binutils`` Needed for the build.
``bison`` Parser generator.
``flex`` Lexical analyser.
``gcc`` C compiler.
``libarchive-dev`` Archive access library.
``libc6-dev`` Files for C language development.
``libexpat1-dev`` An XML processing library.
``m4`` Macro processor.
``pmake`` A ``make`` that uses BSD-make syntax.
``python-yaml`` A YAML library for Python.
``sharutils`` For ``uudecode``.
=================== =====================================
The following command line may be used to install the necessary
pre-requisites::
% sudo apt-get install binutils bison flex gcc libarchive-dev \
libc6-dev m4 pmake
- To build and run the test suite:
#. The current release of the `Test Execution Toolkit`_, needs
to be downloaded and unpacked into the ``test/tet/``
directory.
#. The following additional packages need to be installed, as
listed in the example command line below::
% sudo apt-get install libexpat1-dev python-yaml sharutils
- To build additional documentation, the ``pgf`` package is
needed::
% sudo apt-get install pgf
:Ubuntu GNU/Linux 11.10:
- The following packages are pre-requisites for building the
sources on Ubuntu GNU/Linux 11.10:
=================== =====================================
**Package** **Description**
=================== =====================================
``bison`` Parser generator.
``flex`` Lexical analyser.
``gcc`` C compiler.
``libarchive-dev`` Archive access library.
``libexpat1-dev`` An XML processing library.
``m4`` Macro processor.
``pmake`` A ``make`` that uses BSD-make syntax.
``python-yaml`` A YAML library for Python.
``sharutils`` For ``uudecode``.
=================== =====================================
The following command line may be used to install the necessary
pre-requisites::
% sudo apt-get install bison flex gcc libarchive-dev \
m4 pmake
- To build and run the test suite:
#. The current release of the `Test Execution Toolkit`_, needs
to be downloaded and unpacked into the ``test/tet/``
directory.
#. The following additional packages need to be installed, as
listed in the example command line below::
% sudo apt-get install libexpat1-dev python-yaml sharutils
- Builds of additional documentation are not currently supported
under Ubuntu GNU/Linux 11.10.
.. _Test Execution Toolkit: http://tetworks.opengroup.org/
.. _OpenGroup: http://www.opengroup.org/
Building the software
---------------------
The software may be built by running **make**.
On `DragonFly BSD`_, FreeBSD_, Minix_, NetBSD_ and OpenBSD_, use::
% make
On Ubuntu GNU/Linux with the **pmake** package installed, use::
% pmake
Testing the software
---------------------
The ``run-tests`` target in the top-level Makefile will build and
execute the test suites that are part of this software.
On `DragonFly BSD`_, FreeBSD_ and NetBSD_, use::
% make run-tests
On Ubuntu GNU/Linux with the **pmake** package installed, use::
% pmake run-tests
Installing the Software
=======================
The software may be installed using the ``install`` target.
On `DragonFly BSD`_, FreeBSD_, Minix_, NetBSD_ and OpenBSD_ use::
% make install
On Ubuntu GNU/Linux with the **pmake** package installed, use::
% pmake install
By default the ``install`` target will install utilities into
``/usr/bin/``, libraries into ``/usr/lib/`` and manual pages into
``/usr/share/man/man[0-9]/``.
The installation directory may be changed using the ``DESTDIR``
variable. For example::
% pmake DESTDIR=$HOME/local install
Additional Information
======================
Additional information about the project may be found on the `project
website`_.
.. _project website: http://elftoolchain.sourceforge.net/
.. $Id: INSTALL 2777 2012-12-12 17:21:36Z jkoshy $
.. Local Variables:
.. mode: rst
.. End:

54
Makefile Normal file
View File

@ -0,0 +1,54 @@
# $Id: Makefile 2872 2013-01-07 13:57:54Z jkoshy $
TOP= .
.include "${TOP}/mk/elftoolchain.os.mk"
# Build configuration information first.
SUBDIR += common
# Build the base libraries next.
SUBDIR += libelf
SUBDIR += libdwarf
# Build additional APIs.
SUBDIR += libelftc
# Build the tools needed for the rest of the build.
SUBDIR += isa
# Build tools after the libraries.
SUBDIR += addr2line
SUBDIR += ar
SUBDIR += brandelf
SUBDIR += cxxfilt
SUBDIR += elfcopy
SUBDIR += elfdump
SUBDIR += findtextrel
SUBDIR += nm
SUBDIR += readelf
SUBDIR += size
SUBDIR += strings
SUBDIR += tools
# Build the test suites.
.if exists(${.CURDIR}/test) && defined(MKTESTS) && ${MKTESTS} == "yes"
SUBDIR += test
.endif
# Build documentation at the end.
.if exists(${.CURDIR}/documentation) && defined(MKDOC) && ${MKDOC} == "yes"
SUBDIR += documentation
.endif
.include "${TOP}/mk/elftoolchain.subdir.mk"
#
# Special top-level targets.
#
# Run the test suites.
.if exists(${.CURDIR}/test) && defined(MKTESTS) && ${MKTESTS} == "yes"
run-tests: all .PHONY
(cd ${.CURDIR}/test; ${MAKE} test)
.endif

127
README Normal file
View File

@ -0,0 +1,127 @@
The Elftoolchain Project
========================
.. contents::
..
Description
-----------
This software implements essential compilation tools and libraries for:
- managing program objects conforming to the ELF_ object format, and
- for managing DWARF_ debugging information in ELF objects.
The project currently implements the following utilities and
libraries:
=========== ============================================
Name Description
=========== ============================================
ar Archive manager.
addr2line Debug tool.
brandelf Manage the ELF brand on executables.
c++filt Translate encoded symbols.
elfcopy Copy and translate between object formats.
elfdump Diagnostic tool.
findtextrel Find undesired text relocations.
libdwarf DWARF access library.
libelf ELF access library.
mcs Manage comment sections.
nm List symbols in an ELF object.
ranlib Add archive symbol tables to an archive.
readelf Display ELF information.
size List object sizes.
strings Extract printable strings.
strip Discard information from ELF objects.
=========== ============================================
.. _ELF: http://en.wikipedia.org/wiki/Executable_and_Linkable_Format
.. _DWARF: http://www.dwarfstd.org/
Project Documentation
---------------------
- Release notes for released versions of this software are present in
the file ``RELEASE-NOTES`` in the current directory.
- The file ``INSTALL`` in the current directory contains instructions
on building and installing this software.
- Reference documentation in the form of manual pages is provided for
the utilities and libraries developed by the project.
- Additional tutorial documentation is present in the
``documentation`` directory.
Tracking Ongoing Development
----------------------------
The project uses subversion_ for its version control system.
The subversion branch for the current set of sources may be accessed
at the following URL:
https://elftoolchain.svn.sourceforge.net/svnroot/elftoolchain/trunk
The project's source tree may be checked out from its repository by
using the ``svn checkout`` command::
% svn checkout https://elftoolchain.svn.sourceforge.net/svnroot/elftoolchain/trunk
Checked-out sources may be kept upto-date by running ``svn update``
inside the source directory::
% svn update
Instructions on building and installing the software are given in the
file ``INSTALL`` in the current directory.
.. _Subversion:
Downloading Released Software
-----------------------------
Released versions of the project's software may also be downloaded
from SourceForge's `file release system`_.
.. _file release system: http://sourceforge.net/projects/elftoolchain/files/
Copyright and License
---------------------
This code is copyright its authors, and is distributed under the `BSD
License`_.
.. _BSD License: http://www.opensource.org/licenses/bsd-license.php
Developer Community
-------------------
The project's developers may be contacted using the mailing list:
``<elftoolchain-developers@lists.sourceforge.net>``.
Reporting Bugs
--------------
Please use our `Trac instance`_ for viewing existing bug reports and
for submitting new bug reports.
.. _`Trac instance`: http://sourceforge.net/apps/trac/elftoolchain/report
Additional Information
----------------------
Additional information about the project may be found on the `project
website`_.
.. _project website: http://elftoolchain.sourceforge.net/
.. $Id: README 2146 2011-11-11 09:39:00Z jkoshy $
.. Local Variables:
.. mode: rst
.. End:

206
RELEASE-NOTES Normal file
View File

@ -0,0 +1,206 @@
.. $Id: RELEASE-NOTES 2599 2012-09-25 06:25:51Z jkoshy $
.. This file contains a template for use when writing release notes.
.. It needs to be updated with release-specific content prior to
.. cutting a release. RST comments (such as this one) also need to be
.. removed prior to the release.
.. The tokens '%.*%' need be replaced with actual content.
Release Notes for Elftoolchain Software Version %.%.%
=====================================================
About The Project
-----------------
The `Elftoolchain project`_ develops BSD-licensed implementations of
essential compilation tools and libraries for handling ELF based program
objects.
About The Release
-----------------
.. Describe the rationale for the release (e.g. new features,
.. significant bug fixes, etc.).
Libraries and Utilities
~~~~~~~~~~~~~~~~~~~~~~~
This release comprises the following libraries and utilities.
================= ==========================================
**Name** **Description**
================= ==========================================
**ar** Archive manager.
**addr2line** Debug tool.
**brandelf** Manage the ELF brand on executables.
**c++filt** Translate encoded symbols.
**elfcopy** Copy and translate between object formats.
**elfdump** Diagnostic tool.
**findtextrel** Find undesired text relocations.
**libdwarf** DWARF access library.
**libelf** ELF access library.
**mcs** Manage comment sections.
**nm** List symbols in an ELF object.
**ranlib** Add archive symbol tables to an archive.
**readelf** Display ELF information.
**size** List object sizes.
**strings** Extract printable strings.
**strip** Discard information from ELF objects.
================= ==========================================
Documentation
~~~~~~~~~~~~~
Each public API and invocable utility has a reference manual entry.
We currently offer %NENTRIES% manual entries, documented in %NFILES%
manual pages.
Additionally, this release contains the following documentation:
================= ==========================================
**Name** **Description**
================= ==========================================
libelf-by-example A tutorial introduction to **libelf**.
================= ==========================================
Test Suites
~~~~~~~~~~~
The release contains the following test suites:
================= ==========================================
**Name** **Description**
================= ==========================================
ar Test the **ar** utility.
elfcopy Test the **elfcopy** utility.
elfdump Test the **elfdump** utility.
libdwarf Test the **libdwarf** library.
libelf Test the **libelf** library.
nm Test the **nm** utility.
================= ==========================================
System Requirements
-------------------
.. Hardware and software requirements for using this software.
This software is designed to run on Unix(TM)-like operating systems
such as the BSD-family of operating systems and GNU/Linux.
This release has been built and tested on the following operating
systems:
==================== =========== ===========================
**Operating System** **Version** **Supported Architectures**
-------------------- ----------- ---------------------------
`DragonFly BSD`_ 2.10.1 i386
FreeBSD_ v8.2 amd64 & i386
Minix_ 3.2.0 i386
NetBSD_ v5.0.2 i386
OpenBSD_ v5.0 i386
Ubuntu_ GNU/Linux 10.04LTS i386 & x86_64
==================== =========== ===========================
Installation and Upgrades
=========================
Installation
------------
Instructions for building and installing this software from source are
described in the file "INSTALL".
Upgrading
---------
.. Special notes about upgrading this software from a prior release.
.. For example, if we introduce any backwards-incompatible behaviour,
.. or if we deprecate existing behaviour.
Release Information
===================
Changes in this release
-----------------------
.. A list of significant changes in the release.
Outstanding Issues
------------------
.. Problems discovered when testing the release.
Known Limitations
-----------------
.. Known limitations.
Test Statistics
---------------
The test summary for this release is presented below:
========= ========= ================ =============== ================ ===================
**Suite** **Tests** **DragonFlyBSD** **FreeBSD** **NetBSD** **Ubuntu 10.04LTS**
========= ========= ================ =============== ================ ===================
ar
elfcopy
elfdump
libdwarf
libelf
nm
========= ========= ================ =============== ================ ===================
Key:
:P:
Test successes.
:F:
Test failures.
:U:
Unresolved tests.
Notes
~~~~~
More Information
================
The project's website is at http://elftoolchain.sourceforge.net/.
Developer Community
-------------------
The project's developers may be contacted using the mailing list:
``<elftoolchain-developers@lists.sourceforge.net>``.
Reporting Bugs
--------------
Please use our `Trac instance`_ for viewing existing bug reports and
for submitting new bug reports.
Copyright and License
=====================
This software is copyright its authors, and is distributed under the
`BSD License`_.
.. _BSD License: http://www.opensource.org/licenses/bsd-license.php
.. _DragonFly BSD: http://www.dragonflybsd.org/
.. _Elftoolchain project: http://elftoolchain.sourceforge.net/
.. _FreeBSD: http://www.freebsd.org/
.. _Minix: http://www.minix3.org/
.. _NetBSD: http://www.netbsd.org/
.. _OpenBSD: http://www.openbsd.org/
.. _`Trac instance`: http://sourceforge.net/apps/trac/elftoolchain/report
.. _Ubuntu: http://www.ubuntu.com/
.. Local Variables:
.. mode: rst
.. End:

15
addr2line/Makefile Normal file
View File

@ -0,0 +1,15 @@
# $Id: Makefile 2066 2011-10-26 15:40:28Z jkoshy $
TOP= ..
PROG= addr2line
SRCS= addr2line.c
WARNS?= 6
DPADD= ${LIBELF} ${LIBELFTC} ${LIBDWARF}
LDADD= -lelftc -ldwarf -lelf
MAN1= addr2line.1
.include "${TOP}/mk/elftoolchain.prog.mk"

159
addr2line/addr2line.1 Normal file
View File

@ -0,0 +1,159 @@
.\" Copyright (c) 2009,2010 Joseph Koshy <jkoshy@users.sourceforge.net>
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer
.\" in this position and unchanged.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\" $Id: addr2line.1 2066 2011-10-26 15:40:28Z jkoshy $
.\"
.Dd July 25, 2010
.Os
.Dt ADDR2LINE 1
.Sh NAME
.Nm addr2line
.Nd translate program addresses to source file names and line numbers
.Sh SYNOPSIS
.Nm
.Op Fl b Ar target | Fl -target Ns = Ns Ar target
.Op Fl e Ar pathname | Fl -exe Ns = Ns Ar pathname
.Op Fl f | Fl -functions
.Op Fl j Ar sectionname | Fl -section Ns = Ns Ar sectionname
.Op Fl s | Fl -basename
.Op Fl C | Fl -demangle
.Op Fl H | Fl -help
.Op Fl V | Fl -version
.Op Ar hexaddress Ns ...
.Sh DESCRIPTION
The
.Nm
utility translates program addresses specified by the command line
arguments
.Ar hexaddress
to their corresponding source file names and line numbers.
If no arguments are given to
.Nm ,
it will read these addresses from standard input.
.Pp
Program addresses specified by arguments
.Ar hexaddress
are encoded using the conventions accepted by
.Xr strtoull 3 .
.Pp
By default,
.Nm
will use the executable
.Dq Pa a.out .
The
.Fl e
option may be used to specified a different ELF object.
.Pp
The
.Nm
utility recognizes the following options:
.Bl -tag -width indent
.It Fl b Ar target | Fl -target Ns = Ns Ar target
This option is recognized by
.Nm
but is ignored.
It is supported for compatibility with GNU binutils.
.It Fl e Ar pathname | Fl -exe Ns = Ns Ar pathname
Use the ELF object specified by argument
.Ar pathname
to translate addresses.
If this option is not specified,
.Nm
will use the file
.Dq Pa a.out .
.It Fl f | Fl -functions
Display function names in addition to file and line number information.
.It Fl j Ar sectionname | Fl -section Ns = Ns Ar sectionname
The values specified by arguments
.Ar hexaddress
are to be treated as offsets into the section named
.Ar sectionname .
.It Fl s | -basename
Display only the base name for each file name.
.It Fl C | Fl -demangle
Demangle C++ names.
.It Fl H | Fl -help
Print a help message.
.It Fl V | Fl -version
Print a version identifier and exit.
.El
.Sh OUTPUT FORMAT
If the
.Fl f
option was not specified,
.Nm
will print the file name and line number for each address specified
on a separate line.
.Pp
If the
.Fl f
option was specified,
.Nm
will print a line containing the name of the function corresponding
to program address
.Ar hexaddress ,
followed by a line with the file name and line number.
.Pp
The
.Nm
utility prints the file name and line number using the format
.Dq FILENAME:LINENUMBER .
.Pp
If a file or function name could not be determined,
.Nm
will print a question mark in their place.
If the line number could not be determined,
.Nm
will print a zero in its place.
.Sh EXAMPLES
To map address 080483c4 in the default executable
.Pa a.out
to a source file name and line number use:
.D1 "% addr2line 080483c4"
.Pp
To map address 080483c4 in executable
.Pa helloworld ,
use:
.D1 "% addr2line -e helloworld 080483c4"
.Pp
To have
.Nm
act as a filter reading addresses from its standard input use:
.D1 "% addr2line"
.Pp
To print the function name corresponding to an address in addition to
its source file and line number use:
.D1 "% addr2line -f 080483c4"
.Sh EXIT STATUS
.Ex -std
.Sh SEE ALSO
.Xr nm 1 ,
.Xr elfdump 1 ,
.Xr elfcopy 1 ,
.Xr strtoull 3
.Sh AUTHORS
The
.Nm
utility was written by
.An "Kai Wang" Aq kaiwang27@users.sourceforge.net .

410
addr2line/addr2line.c Normal file
View File

@ -0,0 +1,410 @@
/*-
* Copyright (c) 2009 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <dwarf.h>
#include <err.h>
#include <fcntl.h>
#include <gelf.h>
#include <getopt.h>
#include <libdwarf.h>
#include <libelftc.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "_elftc.h"
ELFTC_VCSID("$Id: addr2line.c 2185 2011-11-19 16:07:16Z jkoshy $");
static struct option longopts[] = {
{"target" , required_argument, NULL, 'b'},
{"demangle", no_argument, NULL, 'C'},
{"exe", required_argument, NULL, 'e'},
{"functions", no_argument, NULL, 'f'},
{"section", required_argument, NULL, 'j'},
{"basename", no_argument, NULL, 's'},
{"help", no_argument, NULL, 'H'},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};
static int demangle, func, base;
static char unknown[] = { '?', '?', '\0' };
static Dwarf_Addr section_base;
#define USAGE_MESSAGE "\
Usage: %s [options] hexaddress...\n\
Map program addresses to source file names and line numbers.\n\n\
Options:\n\
-b TGT | --target=TGT (Accepted but ignored).\n\
-e EXE | --exec=EXE Use program \"EXE\" to translate addresses.\n\
-f | --functions Display function names.\n\
-j NAME | --section=NAME Values are offsets into section \"NAME\".\n\
-s | --basename Only show the base name for each file name.\n\
-C | --demangle Demangle C++ names.\n\
-H | --help Print a help message.\n\
-V | --version Print a version identifier and exit.\n"
static void
usage(void)
{
(void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
exit(1);
}
static void
version(void)
{
fprintf(stderr, "%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version());
exit(0);
}
static void
search_func(Dwarf_Debug dbg, Dwarf_Die die, Dwarf_Addr addr,
const char **rlt_func)
{
Dwarf_Die ret_die, spec_die;
Dwarf_Error de;
Dwarf_Half tag;
Dwarf_Unsigned lopc, hipc;
Dwarf_Off ref;
Dwarf_Attribute sub_at, spec_at;
char *func0;
int ret;
if (*rlt_func != NULL)
return;
if (dwarf_tag(die, &tag, &de)) {
warnx("dwarf_tag: %s", dwarf_errmsg(de));
goto cont_search;
}
if (tag == DW_TAG_subprogram) {
if (dwarf_attrval_unsigned(die, DW_AT_low_pc, &lopc, &de) ||
dwarf_attrval_unsigned(die, DW_AT_high_pc, &hipc, &de))
goto cont_search;
if (addr < lopc || addr >= hipc)
goto cont_search;
/* Found it! */
*rlt_func = unknown;
ret = dwarf_attr(die, DW_AT_name, &sub_at, &de);
if (ret == DW_DLV_ERROR)
return;
if (ret == DW_DLV_OK) {
if (dwarf_formstring(sub_at, &func0, &de))
*rlt_func = unknown;
else
*rlt_func = func0;
return;
}
/*
* If DW_AT_name is not present, but DW_AT_specification is
* present, then probably the actual name is in the DIE
* referenced by DW_AT_specification.
*/
if (dwarf_attr(die, DW_AT_specification, &spec_at, &de))
return;
if (dwarf_global_formref(spec_at, &ref, &de))
return;
if (dwarf_offdie(dbg, ref, &spec_die, &de))
return;
if (dwarf_attrval_string(spec_die, DW_AT_name, rlt_func, &de))
*rlt_func = unknown;
return;
}
cont_search:
/* Search children. */
ret = dwarf_child(die, &ret_die, &de);
if (ret == DW_DLV_ERROR)
errx(EXIT_FAILURE, "dwarf_child: %s", dwarf_errmsg(de));
else if (ret == DW_DLV_OK)
search_func(dbg, ret_die, addr, rlt_func);
/* Search sibling. */
ret = dwarf_siblingof(dbg, die, &ret_die, &de);
if (ret == DW_DLV_ERROR)
errx(EXIT_FAILURE, "dwarf_siblingof: %s", dwarf_errmsg(de));
else if (ret == DW_DLV_OK)
search_func(dbg, ret_die, addr, rlt_func);
}
static void
translate(Dwarf_Debug dbg, const char* addrstr)
{
Dwarf_Die die;
Dwarf_Line *lbuf;
Dwarf_Error de;
Dwarf_Half tag;
Dwarf_Unsigned lopc, hipc, addr, lineno, plineno;
Dwarf_Signed lcount;
Dwarf_Addr lineaddr, plineaddr;
const char *funcname;
char *file, *file0, *pfile;
char demangled[1024];
int i, ret;
addr = strtoull(addrstr, NULL, 16);
addr += section_base;
lineno = 0;
file = unknown;
while ((ret = dwarf_next_cu_header(dbg, NULL, NULL, NULL, NULL, NULL,
&de)) == DW_DLV_OK) {
die = NULL;
while (dwarf_siblingof(dbg, die, &die, &de) == DW_DLV_OK) {
if (dwarf_tag(die, &tag, &de) != DW_DLV_OK) {
warnx("dwarf_tag failed: %s",
dwarf_errmsg(de));
goto out;
}
/* XXX: What about DW_TAG_partial_unit? */
if (tag == DW_TAG_compile_unit)
break;
}
if (die == NULL) {
warnx("could not find DW_TAG_compile_unit die");
goto out;
}
if (!dwarf_attrval_unsigned(die, DW_AT_low_pc, &lopc, &de) &&
!dwarf_attrval_unsigned(die, DW_AT_high_pc, &hipc, &de)) {
/*
* Check if the address falls into the PC range of
* this CU.
*/
if (addr < lopc || addr >= hipc)
continue;
}
if (dwarf_srclines(die, &lbuf, &lcount, &de) != DW_DLV_OK) {
warnx("dwarf_srclines: %s", dwarf_errmsg(de));
goto out;
}
plineaddr = ~0ULL;
plineno = 0;
pfile = unknown;
for (i = 0; i < lcount; i++) {
if (dwarf_lineaddr(lbuf[i], &lineaddr, &de)) {
warnx("dwarf_lineaddr: %s",
dwarf_errmsg(de));
goto out;
}
if (dwarf_lineno(lbuf[i], &lineno, &de)) {
warnx("dwarf_lineno: %s",
dwarf_errmsg(de));
goto out;
}
if (dwarf_linesrc(lbuf[i], &file0, &de)) {
warnx("dwarf_linesrc: %s",
dwarf_errmsg(de));
} else
file = file0;
if (addr == lineaddr)
goto out;
else if (addr < lineaddr && addr > plineaddr) {
lineno = plineno;
file = pfile;
goto out;
}
plineaddr = lineaddr;
plineno = lineno;
pfile = file;
}
}
out:
funcname = NULL;
if (ret == DW_DLV_OK && func)
search_func(dbg, die, addr, &funcname);
if (func) {
if (funcname == NULL)
funcname = unknown;
if (demangle &&
!elftc_demangle(funcname, demangled, sizeof(demangled), 0))
printf("%s\n", demangled);
else
printf("%s\n", funcname);
}
(void) printf("%s:%ju\n", base ? basename(file) : file, lineno);
/*
* Reset internal CU pointer, so we will start from the first CU
* next round.
*/
while (ret != DW_DLV_NO_ENTRY) {
if (ret == DW_DLV_ERROR)
errx(EXIT_FAILURE, "dwarf_next_cu_header: %s",
dwarf_errmsg(de));
ret = dwarf_next_cu_header(dbg, NULL, NULL, NULL, NULL, NULL,
&de);
}
}
static void
find_section_base(const char *exe, Elf *e, const char *section)
{
Dwarf_Addr off;
Elf_Scn *scn;
GElf_Ehdr eh;
GElf_Shdr sh;
size_t shstrndx;
int elferr;
const char *name;
if (gelf_getehdr(e, &eh) != &eh) {
warnx("gelf_getehdr failed: %s", elf_errmsg(-1));
return;
}
if (!elf_getshstrndx(e, &shstrndx)) {
warnx("elf_getshstrndx failed: %s", elf_errmsg(-1));
return;
}
(void) elf_errno();
off = 0;
scn = NULL;
while ((scn = elf_nextscn(e, scn)) != NULL) {
if (gelf_getshdr(scn, &sh) == NULL) {
warnx("gelf_getshdr failed: %s", elf_errmsg(-1));
continue;
}
if ((name = elf_strptr(e, shstrndx, sh.sh_name)) == NULL)
goto next;
if (!strcmp(section, name)) {
if (eh.e_type == ET_EXEC || eh.e_type == ET_DYN) {
/*
* For executables, section base is the virtual
* address of the specified section.
*/
section_base = sh.sh_addr;
} else if (eh.e_type == ET_REL) {
/*
* For relocatables, section base is the
* relative offset of the specified section
* to the start of the first section.
*/
section_base = off;
} else
warnx("unknown e_type %u", eh.e_type);
return;
}
next:
off += sh.sh_size;
}
elferr = elf_errno();
if (elferr != 0)
warnx("elf_nextscn failed: %s", elf_errmsg(elferr));
errx(EXIT_FAILURE, "%s: cannot find section %s", exe, section);
}
int
main(int argc, char **argv)
{
Elf *e;
Dwarf_Debug dbg;
Dwarf_Error de;
const char *exe, *section;
char line[1024];
int fd, i, opt;
exe = NULL;
section = NULL;
while ((opt = getopt_long(argc, argv, "b:Ce:fj:sHV", longopts, NULL)) !=
-1) {
switch (opt) {
case 'b':
/* ignored */
break;
case 'C':
demangle = 1;
break;
case 'e':
exe = optarg;
break;
case 'f':
func = 1;
break;
case 'j':
section = optarg;
break;
case 's':
base = 1;
break;
case 'H':
usage();
case 'V':
version();
default:
usage();
}
}
argv += optind;
argc -= optind;
if (exe == NULL)
exe = "a.out";
if ((fd = open(exe, O_RDONLY)) < 0)
err(EXIT_FAILURE, "%s", exe);
if (dwarf_init(fd, DW_DLC_READ, NULL, NULL, &dbg, &de))
errx(EXIT_FAILURE, "dwarf_init: %s", dwarf_errmsg(de));
if (dwarf_get_elf(dbg, &e, &de) != DW_DLV_OK)
errx(EXIT_FAILURE, "dwarf_get_elf: %s", dwarf_errmsg(de));
if (section)
find_section_base(exe, e, section);
else
section_base = 0;
if (argc > 0)
for (i = 0; i < argc; i++)
translate(dbg, argv[i]);
else
while (fgets(line, sizeof(line), stdin) != NULL)
translate(dbg, line);
dwarf_finish(dbg, &de);
(void) elf_end(e);
exit(0);
}

35
ar/Makefile Normal file
View File

@ -0,0 +1,35 @@
# $Id: Makefile 2741 2012-12-10 18:47:00Z jkoshy $
TOP= ..
PROG= ar
SRCS= ar.c read.c util.c write.c
LSRC= acplex.l
YSRC= acpyacc.y
WARNS?= 5
DPADD= ${LIBARCHIVE} ${LIBELFTC} ${LIBELF}
LDADD= -larchive -lelftc -lelf
CFLAGS+=-I. -I${.CURDIR}
LINKS= ${BINDIR}/ar ${BINDIR}/ranlib
EXTRA_TARGETS= ranlib
CLEANFILES+= ${EXTRA_TARGETS}
MAN= ar.1 ranlib.1 ar.5
all: ${EXTRA_TARGETS}
${EXTRA_TARGETS}: ${PROG}
ln -s ${PROG} ${.TARGET}
.include "${TOP}/mk/elftoolchain.prog.mk"
.if ${OS_HOST} == "OpenBSD"
CFLAGS+= -I/usr/local/include
LDFLAGS+= -L/usr/local/lib
.endif

84
ar/acplex.l Normal file
View File

@ -0,0 +1,84 @@
%{
/*-
* Copyright (c) 2008 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "_elftc.h"
ELFTC_VCSID("$Id: acplex.l 2130 2011-11-10 06:56:46Z jkoshy $");
#include "acpyacc.h"
#define YY_NO_UNPUT
#if !defined(ELFTC_BROKEN_YY_NO_INPUT)
#define YY_NO_INPUT
#endif
int lineno = 1;
int yylex(void);
%}
%option noyywrap
%%
ADDLIB|addlib { return (ADDLIB); }
ADDMOD|addmod { return (ADDMOD); }
CLEAR|clear { return (CLEAR); }
CREATE|create { return (CREATE); }
DELETE|delete { return (DELETE); }
DIRECTORY|directory { return (DIRECTORY); }
END|end { return (END); }
EXTRACT|extract { return (EXTRACT); }
LIST|list { return (LIST); }
OPEN|open { return (OPEN); }
REPLACE|replace { return (REPLACE); }
VERBOSE|verbose { return (VERBOSE); }
SAVE|save { return (SAVE); }
"(" { return (LP); }
")" { return (RP); }
"," { return (COMMA); }
[-_A-Za-z0-9/:$.\\]+ {
yylval.str = strdup(yytext);
if (yylval.str == NULL)
err(EXIT_FAILURE, "strdup failed");
return (FNAME);
}
[ \t] /* whitespace */
"*".* /* comment */
";".* /* comment */
"+\n" { lineno++; /* '+' is line continuation char */ }
"\n" { lineno++; return (EOL); }

661
ar/acpyacc.y Normal file
View File

@ -0,0 +1,661 @@
%{
/*-
* Copyright (c) 2008 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <archive.h>
#include <archive_entry.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "libelftc.h"
#include "ar.h"
ELFTC_VCSID("$Id");
#define TEMPLATE "arscp.XXXXXXXX"
struct list {
char *str;
struct list *next;
};
extern int yylex(void);
extern int yyparse(void);
static void yyerror(const char *);
static void arscp_addlib(char *archive, struct list *list);
static void arscp_addmod(struct list *list);
static void arscp_clear(void);
static void arscp_create(char *in, char *out);
static void arscp_delete(struct list *list);
static void arscp_dir(char *archive, struct list *list, char *rlt);
static void arscp_end(int eval);
static void arscp_extract(struct list *list);
static void arscp_free_argv(void);
static void arscp_free_mlist(struct list *list);
static void arscp_list(void);
static struct list *arscp_mlist(struct list *list, char *str);
static void arscp_mlist2argv(struct list *list);
static int arscp_mlist_len(struct list *list);
static void arscp_open(char *fname);
static void arscp_prompt(void);
static void arscp_replace(struct list *list);
static void arscp_save(void);
static int arscp_target_exist(void);
extern int lineno;
static struct bsdar *bsdar;
static char *target;
static char *tmpac;
static int interactive;
static int verbose;
%}
%token ADDLIB
%token ADDMOD
%token CLEAR
%token CREATE
%token DELETE
%token DIRECTORY
%token END
%token EXTRACT
%token LIST
%token OPEN
%token REPLACE
%token VERBOSE
%token SAVE
%token LP
%token RP
%token COMMA
%token EOL
%token <str> FNAME
%type <list> mod_list
%union {
char *str;
struct list *list;
}
%%
begin
: { arscp_prompt(); } ar_script
;
ar_script
: cmd_list
|
;
mod_list
: FNAME { $$ = arscp_mlist(NULL, $1); }
| mod_list separator FNAME { $$ = arscp_mlist($1, $3); }
;
separator
: COMMA
|
;
cmd_list
: rawcmd
| cmd_list rawcmd
;
rawcmd
: cmd EOL { arscp_prompt(); }
;
cmd
: addlib_cmd
| addmod_cmd
| clear_cmd
| create_cmd
| delete_cmd
| directory_cmd
| end_cmd
| extract_cmd
| list_cmd
| open_cmd
| replace_cmd
| verbose_cmd
| save_cmd
| invalid_cmd
| empty_cmd
| error
;
addlib_cmd
: ADDLIB FNAME LP mod_list RP { arscp_addlib($2, $4); }
| ADDLIB FNAME { arscp_addlib($2, NULL); }
;
addmod_cmd
: ADDMOD mod_list { arscp_addmod($2); }
;
clear_cmd
: CLEAR { arscp_clear(); }
;
create_cmd
: CREATE FNAME { arscp_create(NULL, $2); }
;
delete_cmd
: DELETE mod_list { arscp_delete($2); }
;
directory_cmd
: DIRECTORY FNAME { arscp_dir($2, NULL, NULL); }
| DIRECTORY FNAME LP mod_list RP { arscp_dir($2, $4, NULL); }
| DIRECTORY FNAME LP mod_list RP FNAME { arscp_dir($2, $4, $6); }
;
end_cmd
: END { arscp_end(EXIT_SUCCESS); }
;
extract_cmd
: EXTRACT mod_list { arscp_extract($2); }
;
list_cmd
: LIST { arscp_list(); }
;
open_cmd
: OPEN FNAME { arscp_open($2); }
;
replace_cmd
: REPLACE mod_list { arscp_replace($2); }
;
save_cmd
: SAVE { arscp_save(); }
;
verbose_cmd
: VERBOSE { verbose = !verbose; }
;
empty_cmd
:
;
invalid_cmd
: FNAME { yyerror(NULL); }
;
%%
/* ARGSUSED */
static void
yyerror(const char *s)
{
(void) s;
printf("Syntax error in archive script, line %d\n", lineno);
}
/*
* The arscp_open() function will first open an archive and check its
* validity. If the archive format is valid, it will call
* arscp_create() to create a temporary copy of the archive.
*/
static void
arscp_open(char *fname)
{
struct archive *a;
struct archive_entry *entry;
int r;
if ((a = archive_read_new()) == NULL)
bsdar_errc(bsdar, 0, "archive_read_new failed");
archive_read_support_compression_none(a);
archive_read_support_format_ar(a);
AC(archive_read_open_file(a, fname, DEF_BLKSZ));
if ((r = archive_read_next_header(a, &entry)))
bsdar_warnc(bsdar, 0, "%s", archive_error_string(a));
AC(archive_read_close(a));
ACV(archive_read_finish(a));
if (r != ARCHIVE_OK)
return;
arscp_create(fname, fname);
}
/*
* Create an archive.
*
* If the parameter 'in' is NULL (the 'CREATE' command), a new empty
* archive will be created. If the parameter 'in' is not NULL (the
* 'OPEN' command), the resulting archive will be a modified version
* of the existing archive.
*/
static void
arscp_create(char *in, char *out)
{
struct archive *a;
int ifd, ofd;
/* Delete the previously created temporary archive, if any. */
if (tmpac) {
if (unlink(tmpac) < 0)
bsdar_errc(bsdar, errno, "unlink failed");
free(tmpac);
}
tmpac = strdup(TEMPLATE);
if (tmpac == NULL)
bsdar_errc(bsdar, errno, "strdup failed");
if ((ofd = mkstemp(tmpac)) < 0)
bsdar_errc(bsdar, errno, "mkstemp failed");
if (in) {
/*
* The 'OPEN' command creates a temporary copy of the
* input archive.
*/
if ((ifd = open(in, O_RDONLY)) < 0 ||
elftc_copyfile(ifd, ofd) < 0) {
bsdar_warnc(bsdar, errno, "'OPEN' failed");
(void) close(ofd);
if (ifd != -1)
(void) close(ifd);
return;
}
(void) close(ifd);
(void) close(ofd);
} else {
/*
* The 'CREATE' command creates an "empty" archive (an
* archive consisting only of the archive header).
*/
if ((a = archive_write_new()) == NULL)
bsdar_errc(bsdar, 0, "archive_write_new failed");
archive_write_set_format_ar_svr4(a);
AC(archive_write_open_fd(a, ofd));
AC(archive_write_close(a));
ACV(archive_write_finish(a));
}
/* Override the previous target, if any. */
if (target)
free(target);
target = out;
bsdar->filename = tmpac;
}
/*
* Add all modules of an archive to the current archive. If the
* parameter 'list' is not NULL, only those modules specified by
* 'list' will be added.
*/
static void
arscp_addlib(char *archive, struct list *list)
{
if (!arscp_target_exist())
return;
arscp_mlist2argv(list);
bsdar->addlib = archive;
ar_write_archive(bsdar, 'A');
arscp_free_argv();
arscp_free_mlist(list);
}
/*
* Add modules to the current archive.
*/
static void
arscp_addmod(struct list *list)
{
if (!arscp_target_exist())
return;
arscp_mlist2argv(list);
ar_write_archive(bsdar, 'q');
arscp_free_argv();
arscp_free_mlist(list);
}
/*
* Delete modules from the current archive.
*/
static void
arscp_delete(struct list *list)
{
if (!arscp_target_exist())
return;
arscp_mlist2argv(list);
ar_write_archive(bsdar, 'd');
arscp_free_argv();
arscp_free_mlist(list);
}
/*
* Extract modules from the current archive.
*/
static void
arscp_extract(struct list *list)
{
if (!arscp_target_exist())
return;
arscp_mlist2argv(list);
ar_read_archive(bsdar, 'x');
arscp_free_argv();
arscp_free_mlist(list);
}
/*
* List the contents of an archive (simple mode).
*/
static void
arscp_list(void)
{
if (!arscp_target_exist())
return;
bsdar->argc = 0;
bsdar->argv = NULL;
/* Always verbose. */
bsdar->options |= AR_V;
ar_read_archive(bsdar, 't');
bsdar->options &= ~AR_V;
}
/*
* List the contents of an archive (advanced mode).
*/
static void
arscp_dir(char *archive, struct list *list, char *rlt)
{
FILE *out;
/* If rlt != NULL, redirect the output to it. */
out = NULL;
if (rlt) {
out = bsdar->output;
if ((bsdar->output = fopen(rlt, "w")) == NULL)
bsdar_errc(bsdar, errno, "fopen %s failed", rlt);
}
bsdar->filename = archive;
if (list)
arscp_mlist2argv(list);
else {
bsdar->argc = 0;
bsdar->argv = NULL;
}
if (verbose)
bsdar->options |= AR_V;
ar_read_archive(bsdar, 't');
bsdar->options &= ~AR_V;
if (rlt) {
if (fclose(bsdar->output) == EOF)
bsdar_errc(bsdar, errno, "fclose %s failed", rlt);
bsdar->output = out;
free(rlt);
}
free(archive);
bsdar->filename = tmpac;
arscp_free_argv();
arscp_free_mlist(list);
}
/*
* Replace modules in the current archive.
*/
static void
arscp_replace(struct list *list)
{
if (!arscp_target_exist())
return;
arscp_mlist2argv(list);
ar_write_archive(bsdar, 'r');
arscp_free_argv();
arscp_free_mlist(list);
}
/*
* Rename the temporary archive to the target archive.
*/
static void
arscp_save(void)
{
mode_t mask;
if (target) {
if (rename(tmpac, target) < 0)
bsdar_errc(bsdar, errno, "rename failed");
/*
* Because mkstemp() creates temporary files with mode
* 0600, we set target archive's mode as per the
* process umask.
*/
mask = umask(0);
umask(mask);
if (chmod(target, 0666 & ~mask) < 0)
bsdar_errc(bsdar, errno, "chmod failed");
free(tmpac);
free(target);
tmpac = NULL;
target= NULL;
bsdar->filename = NULL;
} else
bsdar_warnc(bsdar, 0, "no open output archive");
}
/*
* Discard the contents of the current archive. This is achieved by
* invoking the 'CREATE' cmd on the current archive.
*/
static void
arscp_clear(void)
{
char *new_target;
if (target) {
new_target = strdup(target);
if (new_target == NULL)
bsdar_errc(bsdar, errno, "strdup failed");
arscp_create(NULL, new_target);
}
}
/*
* Quit ar(1). Note that the 'END' cmd will not 'SAVE' the current
* archive before exiting.
*/
static void
arscp_end(int eval)
{
if (target)
free(target);
if (tmpac) {
if (unlink(tmpac) == -1)
bsdar_errc(bsdar, errno, "unlink %s failed", tmpac);
free(tmpac);
}
exit(eval);
}
/*
* Check if a target was specified, i.e, whether an 'OPEN' or 'CREATE'
* had been issued by the user.
*/
static int
arscp_target_exist(void)
{
if (target)
return (1);
bsdar_warnc(bsdar, 0, "no open output archive");
return (0);
}
/*
* Construct the list of modules.
*/
static struct list *
arscp_mlist(struct list *list, char *str)
{
struct list *l;
l = malloc(sizeof(*l));
if (l == NULL)
bsdar_errc(bsdar, errno, "malloc failed");
l->str = str;
l->next = list;
return (l);
}
/*
* Calculate the length of an mlist.
*/
static int
arscp_mlist_len(struct list *list)
{
int len;
for(len = 0; list; list = list->next)
len++;
return (len);
}
/*
* Free the space allocated for a module list.
*/
static void
arscp_free_mlist(struct list *list)
{
struct list *l;
/* Note: list->str was freed in arscp_free_argv(). */
for(; list; list = l) {
l = list->next;
free(list);
}
}
/*
* Convert a module list to an 'argv' array.
*/
static void
arscp_mlist2argv(struct list *list)
{
char **argv;
int i, n;
n = arscp_mlist_len(list);
argv = malloc(n * sizeof(*argv));
if (argv == NULL)
bsdar_errc(bsdar, errno, "malloc failed");
/* Note that module names are stored in reverse order. */
for(i = n - 1; i >= 0; i--, list = list->next) {
if (list == NULL)
bsdar_errc(bsdar, errno, "invalid mlist");
argv[i] = list->str;
}
bsdar->argc = n;
bsdar->argv = argv;
}
/*
* Free the space allocated for an argv array and its elements.
*/
static void
arscp_free_argv(void)
{
int i;
for(i = 0; i < bsdar->argc; i++)
free(bsdar->argv[i]);
free(bsdar->argv);
}
/*
* Show a prompt if we are in interactive mode.
*/
static void
arscp_prompt(void)
{
if (interactive) {
printf("AR >");
fflush(stdout);
}
}
/*
* The main function implementing script mode.
*/
void
ar_mode_script(struct bsdar *ar)
{
bsdar = ar;
interactive = isatty(fileno(stdin));
while(yyparse()) {
if (!interactive)
arscp_end(EXIT_FAILURE);
}
/* Script ends without END */
arscp_end(EXIT_SUCCESS);
}

603
ar/ar.1 Normal file
View File

@ -0,0 +1,603 @@
.\" Copyright (c) 2007,2009-2012 Joseph Koshy. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" This software is provided by Joseph Koshy ``as is'' and
.\" any express or implied warranties, including, but not limited to, the
.\" implied warranties of merchantability and fitness for a particular purpose
.\" are disclaimed. in no event shall Joseph Koshy be liable
.\" for any direct, indirect, incidental, special, exemplary, or consequential
.\" damages (including, but not limited to, procurement of substitute goods
.\" or services; loss of use, data, or profits; or business interruption)
.\" however caused and on any theory of liability, whether in contract, strict
.\" liability, or tort (including negligence or otherwise) arising in any way
.\" out of the use of this software, even if advised of the possibility of
.\" such damage.
.\"
.\" $Id: ar.1 2742 2012-12-10 18:47:36Z jkoshy $
.\"
.Dd December 10, 2012
.Os
.Dt AR 1
.Sh NAME
.Nm ar
.Nd manage archives
.Sh SYNOPSIS
.Nm
.Fl d
.Op Fl T
.Op Fl f
.Op Fl j
.Op Fl v
.Op Fl z
.Ar archive
.Ar
.Nm
.Fl m
.Op Fl T
.Op Fl a Ar position-after
.Op Fl b Ar position-before
.Op Fl f
.Op Fl i Ar position-before
.Op Fl j
.Op Fl s | Fl S
.Op Fl z
.Ar archive
.Ar
.Nm
.Fl p
.Op Fl T
.Op Fl f
.Op Fl v
.Ar archive
.Op Ar
.Nm
.Fl q
.Op Fl T
.Op Fl c
.Op Fl D
.Op Fl f
.Op Fl F Ar flavor | Fl -flavor Ar flavor
.Op Fl s | Fl S
.Op Fl v
.Op Fl z
.Ar archive
.Ar
.Nm
.Fl r
.Op Fl T
.Op Fl a Ar position-after
.Op Fl b Ar position-before
.Op Fl c
.Op Fl D
.Op Fl f
.Op Fl F Ar flavor | Fl -flavor Ar flavor
.Op Fl i Ar position-before
.Op Fl j
.Op Fl s | Fl S
.Op Fl u
.Op Fl v
.Op Fl z
.Ar archive
.Ar
.Nm
.Fl s
.Op Fl D
.Op Fl j
.Op Fl z
.Ar archive
.Nm
.Fl t
.Op Fl f
.Op Fl T
.Op Fl v
.Ar archive
.Op Ar
.Nm
.Fl x
.Op Fl C
.Op Fl T
.Op Fl f
.Op Fl o
.Op Fl u
.Op Fl v
.Ar archive
.Op Ar
.Nm
.Fl M
.Nm
.Fl V
.Sh DESCRIPTION
The
.Nm
utility creates and maintains groups of files combined into an
archive.
Once an archive has been created, new files can be added to it, and
existing files can be extracted, deleted or replaced.
.Pp
Files are named in the archive by their last file name component,
so if a file referenced by a path containing a
.Dq /
is archived, it will be named by the last component of the path.
Similarly when matching paths listed on the command line against
file names stored in the archive, only the last component of the
path will be compared.
.Pp
The normal use of
.Nm
is for the creation and maintenance of libraries suitable for use
with the link editor
.Xr ld 1 ,
although it is not restricted to this purpose.
The
.Nm
utility can create and manage an archive symbol table (see
.Xr ar 5 )
used to speed up link editing operations.
If a symbol table is present in an archive, it will be
kept up-to-date by subsequent operations on the archive.
.Sh OPTIONS
The
.Nm
utility supports the following options:
.Bl -tag -width indent
.It Fl a Ar member-after
When used with option
.Fl m
this option specifies that the archive members specified by
arguments
.Ar
are moved to after the archive member named by argument
.Ar member-after .
When used with option
.Fl r
this option specifies that the files specified by arguments
.Ar
are added after the archive member named by argument
.Ar member-after .
.It Fl b Ar member-before
When used with option
.Fl m
this option specifies that the archive members specified by
arguments
.Ar
are moved to before the archive member named by argument
.Ar member-before .
When used with option
.Fl r
this option specifies that the files specified by arguments
.Ar
are added before the archive member named by argument
.Ar member-before .
.It Fl c
Suppress the informational message printed when a new archive is
created using the
.Fl r
and
.Fl q
options.
.It Fl C
Prevent extracted files from replacing like-named files
in the file system.
.It Fl d
Delete the members named by arguments
.Ar
from the archive specified by argument
.Ar archive .
The archive's symbol table, if present, is updated to reflect
the new contents of the archive.
.It Fl D
When used in combination with the
.Fl r
or
.Fl q
option, insert 0's instead of the real mtime, uid and gid values
and 0644 instead of file mode from the members named by arguments
.Ar .
This ensures that checksums on the resulting archives are reproducible
when member contents are identical.
.It Fl f
Synonymous with option
.Fl T .
.It Fl F Ar flavor | Fl -flavor Ar flavor
Create archives with the specified archive format.
Legal values for argument
.Ar flavor
are:
.Bl -tag -width indent -compact
.It Ar bsd
Create BSD format archives.
.It Ar gnu
An alias for
.Ar svr4 .
.It Ar svr4
Create SVR4 format archives.
.El
If this option is not specified,
.Nm
will create archives using the SVR4 format.
.It Fl i Ar member-before
Synonymous with option
.Fl b .
.It Fl j
This option is accepted for compatibility with the
.Tn FreeBSD
version of the
.Nm
utility, but is ignored.
.It Fl l
This option is accepted for compatibility with GNU
.Xr ar 1 ,
but is ignored.
.It Fl m
Move archive members specified by arguments
.Ar
within the archive.
If a position has been specified by one of the
.Fl a ,
.Fl b
or
.Fl i
options, the members are moved to before or after the specified
position.
If no position has been specified, the specified members are moved
to the end of the archive.
If the archive has a symbol table, it is updated to reflect the
new contents of the archive.
.It Fl M
Read and execute MRI librarian commands from standard input.
The commands understood by the
.Nm
utility are described in the section
.Sx "MRI Librarian Commands" .
.It Fl o
Preserve the original modification times of members when extracting
them.
.It Fl p
Write the contents of the specified archive members named by
arguments
.Ar
to standard output.
If no members were specified, the contents of all the files in the
archive are written in the order they appear in the archive.
.It Fl q
Append the files specified by arguments
.Ar
to the archive specified by argument
.Ar archive
without checking if the files already exist in the archive.
The archive symbol table will be updated as needed.
If the file specified by the argument
.Ar archive
does not already exist, a new archive will be created.
.It Fl r
Replace (add) the files specified by arguments
.Ar
in the archive specified by argument
.Ar archive ,
creating the archive if necessary.
Replacing existing members will not change the order of members within
the archive.
If a file named in arguments
.Ar
does not exist, existing members in the archive that match that
name are not changed.
New files are added to the end of the archive unless one of the
positioning options
.Fl a ,
.Fl b
or
.Fl i
is specified.
The archive symbol table, if it exists, is updated to reflect the
new state of the archive.
.It Fl s
Add an archive symbol table (see
.Xr ar 5 )
to the archive specified by argument
.Ar archive .
Invoking
.Nm
with the
.Fl s
option alone is equivalent to invoking
.Xr ranlib 1 .
.It Fl S
Do not generate an archive symbol table.
.It Fl t
For
.Nm ,
list the files specified by arguments
.Ar
in the order in which they appear in the archive, one per line.
If no files are specified, all files in the archive are listed.
.It Fl T
Use only the first fifteen characters of the archive member name or
command line file name argument when naming archive members.
.It Fl u
Conditionally update the archive or extract members.
When used with the
.Fl r
option, files named by arguments
.Ar
will be replaced in the archive if they are newer than their
archived versions.
When used with the
.Fl x
option, the members specified by arguments
.Ar
will be extracted only if they are newer than the corresponding
files in the file system.
.It Fl v
Provide verbose output.
When used with the
.Fl d ,
.Fl m ,
.Fl q
or
.Fl x
options,
.Nm
gives a file-by-file description of the archive modification being
performed, which consists of three white-space seperated fields:
the option letter, a dash
.Dq "-" ,
and the file name.
When used with the
.Fl r
option,
.Nm
displays the description as above, but the initial letter is an
.Dq a
if the file is added to the archive, or an
.Dq r
if the file replaces a file already in the archive.
When used with the
.Fl p
option, the name of the file enclosed in
.Dq <
and
.Dq >
characters is written to standard output preceded by a single newline
character and followed by two newline characters.
The contents of the named file follow the file name.
When used with the
.Fl t
option,
.Nm
displays eight whitespace separated fields:
the file permissions as displayed by
.Xr strmode 3 ,
decimal user and group IDs separated by a slash (
.Dq / Ns ) ,
the file size in bytes, the file modification time in
.Xr strftime 3
format
.Dq "%b %e %H:%M %Y" ,
and the name of the file.
.It Fl V
Print a version identifier and exit.
.It Fl x
Extract archive members specified by arguments
.Ar
into the current directory.
If no members have been specified, extract all members of the archive.
If the file corresponding to an extracted member does not exist it
will be created.
If the file corresponding to an extracted member does exist, its owner
and group will not be changed while its contents will be overwritten
and its permissions will set to that entered in the archive.
The file's access and modification time would be that of the time
of extraction unless the
.Fl o
option was specified.
.It Fl z
This option is accepted for compatibility with the
.Tn FreeBSD
version of the
.Nm
utility, but is ignored.
.El
.Ss "MRI Librarian Commands"
If the
.Fl M
option is specified, the
.Nm
utility will read and execute commands from its standard input.
If standard input is a terminal, the
.Nm
utility will display the prompt
.Dq Li "AR >"
before reading a line, and will continue operation even if errors are
encountered.
If standard input is not a terminal, the
.Nm
utility will not display a prompt and will terminate execution on
encountering an error.
.Pp
Each input line contains a single command.
Words in an input line are separated by whitespace characters.
The first word of the line is the command, the remaining words are
the arguments to the command.
The command word may be specified in either case.
Arguments may be separated by commas or blanks.
.Pp
Empty lines are allowed and are ignored.
Long lines are continued by ending them with the
.Dq Li +
character.
.Pp
The
.Dq Li *
and
.Dq Li "\;"
characters start a comment.
Comments extend till the end of the line.
.Pp
When executing an MRI librarian script the
.Nm
utility works on a temporary copy of an archive.
Changes to the copy are made permanent using the
.Ic save
command.
.Pp
Commands understood by the
.Nm
utility are:
.Bl -tag -width indent
.It Ic addlib Ar archive | Ic addlib Ar archive Pq Ar member Oo Li , Ar member Oc Ns ...
Add the contents of the archive named by argument
.Ar archive
to the current archive.
If specific members are named using the arguments
.Ar member ,
then those members are added to the current archive.
If no members are specified, the entire contents of the archive
are added to the current archive.
.It Ic addmod Ar member Oo Li , Ar member Oc Ns ...
Add the files named by arguments
.Ar member
to the current archive.
.It Ic clear
Discard all the contents of the current archive.
.It Ic create Ar archive
Create a new archive named by the argument
.Ar archive ,
and makes it the current archive.
If the named archive already exists, it will be overwritten
when the
.Ic save
command is issued.
.It Ic delete Ar module Oo Li , Ar member Oc Ns ...
Delete the modules named by the arguments
.Ar member
from the current archive.
.It Ic directory Ar archive Po Ar member Oo Li , Ar member Oc Ns ... Pc Op Ar outputfile
List each named module in the archive.
The format of the output depends on the verbosity setting set using
the
.Ic verbose
command.
Output is sent to standard output, or to the file specified by
argument
.Ar outputfile .
.It Ic end
Exit successfully from the
.Nm
utility.
Any unsaved changes to the current archive will be discarded.
.It Ic extract Ar member Oo Li , Ar member Oc Ns ...
Extract the members named by the arguments
.Ar member
from the current archive.
.It Ic list
Display the contents of the current archive in verbose style.
.It Ic open Ar archive
Open the archive named by argument
.Ar archive
and make it the current archive.
.It Ic replace Ar member Oo Li , Ar member Oc Ns ...
Replace named members in the current archive with the files specified
by arguments
.Ar member .
The files must be present in the current directory and the named
modules must already exist in the current archive.
.It Ic save
Commit all changes to the current archive.
.It Ic verbose
Toggle the verbosity of the
.Ic directory
command.
.El
.Sh EXAMPLES
To create a new archive
.Pa ex.a
containing three files
.Pa ex1.o ,
.Pa ex2.o
and
.Pa ex3.o ,
use:
.Dl "ar -rc ex.a ex1.o ex2.o ex3.o"
.Pp
To add an archive symbol table to an existing archive
.Pa ex.a ,
use:
.Dl "ar -s ex.a"
.Pp
To delete file
.Pa ex1.o
from archive
.Pa ex.a ,
use:
.D1 "ar -d ex.a ex1.o"
.Pp
To verbosely list the contents of archive
.Pa ex.a ,
use:
.D1 "ar -tv ex.a"
.Pp
To create a new archive
.Pa ex.a
containing the files
.Pa ex1.o ,
and
.Pa ex2.o ,
using MRI librarian commands, use the following script:
.Bd -literal -offset indent
create ex.a * specify the output archive
addmod ex1.o ex2.o * add modules
save * save pending changes
end * exit the utility
.Ed
.Sh DIAGNOSTICS
.Ex -std
.Sh SEE ALSO
.Xr ld 1 ,
.Xr ranlib 1 ,
.Xr archive 3 ,
.Xr elf 3 ,
.Xr strftime 3 ,
.Xr strmode 3 ,
.Xr ar 5
.Sh STANDARDS COMPLIANCE
The
.Nm
utility's support for the
.Fl a ,
.Fl b ,
.Fl c ,
.Fl i ,
.Fl m ,
.Fl p ,
.Fl q ,
.Fl r ,
.Fl s ,
.Fl t ,
.Fl u ,
.Fl v ,
.Fl C
and
.Fl T
options is believed to be compliant with
.St -p1003.2 .
.Sh HISTORY
An
.Nm
command first appeared in AT&T UNIX Version 1.
In
.Fx 8.0 ,
.An "Kai Wang" Aq kaiw@FreeBSD.org
reimplemented
.Nm
using the
.Lb libarchive
and the
.Lb libelf .

327
ar/ar.5 Normal file
View File

@ -0,0 +1,327 @@
.\" Copyright (c) 2010 Joseph Koshy. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $Id: ar.5 2066 2011-10-26 15:40:28Z jkoshy $
.\"
.Dd November 28, 2010
.Os
.Dt AR 5
.Sh NAME
.Nm ar
.Nd archive file format for
.Xr ar 1
and
.Xr ranlib 1
.Sh SYNOPSIS
.In ar.h
.Sh DESCRIPTION
.Xr ar 1
archives are created and managed by the
.Xr ar 1
and
.Xr ranlib 1
utilities.
These archives are typically used during program development to
hold libraries of program objects.
An
.Xr ar 1
archive is contained in a single operating system file.
.Pp
This manual page documents two variants of the
.Xr ar 1
archive format: the BSD archive format, and the SVR4/GNU archive
format.
.Pp
In both variants the archive file starts with an identifying byte
sequence of the seven ASCII characters
.Sq Li "!<arch>"
followed by a ASCII linefeed character
.Po
see the constant
.Dq ARMAG
in the header file
.In ar.h
.Pc .
.Pp
Archive members follow the initial identifying byte sequence.
Each archive member is prefixed by a fixed size header describing the
file attributes associated with the member.
.Ss "Archive Headers"
An archive header describes the file attributes for the archive member that
follows it.
The
.Xr ar 5
format only supports a limited number of attributes: the file name,
the file creation time stamp, the uid and gid of the creator, the file
mode and the file size.
.Pp
Archive headers are placed at an even byte offset in the archive file.
If the data for an archive member ends at an odd byte offset, then a
padding byte with value 0x0A is used to position the next archive
header on an even byte offset.
.Pp
An archive header comprises the following fixed sized fields:
.Bl -tag -width "Li ar_name"
.It Ar ar_name
(16 bytes) The file name of the archive member.
The format of this field varies between the BSD and SVR4/GNU formats and
is described in more detail in the section
.Sx "Representing File Names"
below.
.It Ar ar_date
(12 bytes) The file modification time for the member in seconds since the
epoch, encoded as a decimal number.
.It Ar ar_uid
(6 bytes) The uid associated with the archive member, encoded as a
decimal number.
.It Ar ar_gid
(6 bytes) The gid associated with the archive member, encoded as a
decimal number.
.It Ar ar_mode
(8 bytes) The file mode for the archive member, encoded as an octal
number.
.It Ar ar_size
(10 bytes) In the SVR4/GNU archive format this field holds the size in
bytes of the archive member, encoded as a decimal number.
In the BSD archive format, for short file names, this field
holds the size in bytes of the archive member, encoded as a decimal
number.
For long file names
.Po
see
.Sx "Representing File Names"
below
.Pc ,
the field contains the combined size of the
archive member and its file name, encoded as a decimal number.
.It Ar ar_fmag
(2 bytes) This field holds 2 bytes with values 0x96 and 0x0A
respectively, marking the end of the header.
.El
.Pp
Unused bytes in the fields of an archive header are set to the value
0x20.
.Ss "Representing File Names"
The BSD and SVR4/GNU variants use different schemes for encoding file
names for members.
.Bl -tag -width "SVR4/GNU"
.It "BSD"
File names that are upto 16 bytes long and which do not contain
embedded spaces are stored directly in the
.Ar ar_name
field of the archive header.
File names that are either longer than 16 bytes or which contain
embedded spaces are stored immediately after the archive header
and the
.Ar ar_name
field of the archive header is set to the string
.Dq "#1/"
followed by a decimal representation of the number of bytes needed for
the file name.
In addition, the
.Ar ar_size
field of the archive header is set to the decimal representation of
the combined sizes of the archive member and the file name.
The file contents of the member follows the file name without further
padding.
.Pp
As an example, if the file name for a member was
.Dq "A B"
and its contents was the string
.Dq "C D" ,
then the
.Ar ar_name
field of the header would contain
.Dq Li "#1/3" ,
the
.Ar ar_size
field of the header would contain
.Dq Li 6 ,
and the bytes immediately following the header would be 0x41, 0x20,
0x42, 0x43, 0x20 and 0x44
.Po
ASCII
.Dq "A BC D"
.Pc .
.It "SVR4/GNU"
File names that are upto 15 characters long are stored directly in the
.Ar ar_name
field of the header, terminated by a
.Dq Li /
character.
.Pp
If the file name is larger than would fit in space for the
.Ar ar_name
field, then the actual file name is kept in the archive
string table
.Po
see
.Sx "Archive String Tables"
below
.Pc ,
and the decimal offset of the file name in the string table is stored
in the
.Ar ar_name
field, prefixed by a
.Dq Li /
character.
.Pp
As an example, if the real file name has been stored at offset 768 in
the archive string table, the
.Ar ar_name
field of the header will contain the string
.Dq /768 .
.El
.Ss "Special Archive Members"
The following archive members are special.
.Bl -tag -width indent
.It Dq Li /
In the SVR4/GNU variant of the archive format, the archive member with
name
.Dq Li /
denotes an archive symbol table.
If present, this member will be the very first member in the
archive.
.It Dq Li //
In the SVR4/GNU variant of the archive format, the archive member with
name
.Dq Li //
denotes the archive string table.
This special member is used to hold filenames that do not fit in the
file name field of the header
.Po
see
.Sx "Representing File Names"
above
.Pc .
If present, this member immediately follows the archive symbol table
if an archive symbol table is present, or is the first member otherwise.
.It Dq Li "__.SYMDEF"
This special member contains the archive symbol table in the BSD
variant of the archive format.
If present, this member will be the very first member in the
archive.
.El
.Ss "Archive String Tables"
An archive string table is used in the SVR4/GNU archive format to hold
file names that are too large to fit into the constraints of the
.Ar ar_name
field of the archive header.
An archive string table contains a sequence of file names.
Each file name in the archive string table is terminated by the
byte sequence 0x2F, 0x0A
.Po
the ASCII string
.Dq "/\en"
.Pc .
No padding is used to separate adjacent file names.
.Ss "Archive Symbol Tables"
Archive symbol tables are used to speed up link editing by providing a
mapping between the program symbols defined in the archive
and the corresponding archive members.
Archive symbol tables are managed by the
.Xr ranlib 1
utility.
.Pp
The format of archive symbol tables is as follows:
.Bl -tag -width "SVR4/GNU"
.It BSD
In the BSD archive format, the archive symbol table comprises
of two parts: a part containing an array of
.Vt "struct ranlib"
descriptors, followed by a part containing a symbol string table.
The sizes and layout of the structures that make up a BSD format
archive symbol table are machine dependent.
.Pp
The part containing
.Vt "struct ranlib"
descriptors begins with a field containing the size in bytes of the
array of
.Vt "struct ranlib"
descriptors encoded as a C
.Vt long
value.
.Pp
The array of
.Vt "struct ranlib"
descriptors follows the size field.
Each
.Vt "struct ranlib"
descriptor describes one symbol.
.Pp
A
.Vt "struct ranlib"
descriptor comprises two fields:
.Bl -tag -width "Ar ran_strx" -compact
.It Ar ran_strx
.Pq C Vt long
This field contains the zero-based offset of the symbol name in the
symbol string table.
.It Ar ran_off
.Pq C Vt long
This field is the file offset to the archive header for the archive
member defining the symbol.
.El
.Pp
The part containing the symbol string table begins with a field
containing the size in bytes of the string table, encoded as a C
.Vt long
value.
This string table follows the size field, and contains
NUL-terminated strings for the symbols in the symbol table.
.It SVR4/GNU
In the SVR4/GNU archive format, the archive symbol table starts with a
4-byte binary value containing the number of entries contained in the
archive symbol table.
This count of entries is stored most significant byte first.
.Pp
Next, there are
.Ar count
4-byte numbers, each stored most significant byte first.
Each number is a binary offset to the archive header for the member in
the archive file for the corresponding symbol table entry.
.Pp
After the binary offset values, there are
.Ar count
NUL-terminated strings in sequence, holding the symbol names for
the corresponding symbol table entries.
.El
.Sh STANDARDS COMPLIANCE
The
.Xr ar 1
archive format is not currently specified by a standard.
.Pp
This manual page documents the
.Xr ar 1
archive formats used by the
.Bx 4.4
and
.Ux SVR4
operating system releases.
.Sh SEE ALSO
.Xr ar 1 ,
.Xr ld 1 ,
.Xr ranlib 1 ,
.Xr elf 3 ,
.Xr elf_getarsym 3 ,
.Xr elf_rand 3

435
ar/ar.c Normal file
View File

@ -0,0 +1,435 @@
/*-
* Copyright (c) 2007 Kai Wang
* Copyright (c) 2007 Tim Kientzle
* Copyright (c) 2007 Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*-
* Copyright (c) 1990, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Hugh Smith at The University of Guelph.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <archive.h>
#include <errno.h>
#include <getopt.h>
#include <libelftc.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "ar.h"
ELFTC_VCSID("$Id: ar.c 2485 2012-04-07 15:54:59Z kaiwang27 $");
enum options
{
OPTION_HELP
};
static struct option longopts[] =
{
{"flavor", required_argument, NULL, 'F'},
{"help", no_argument, NULL, OPTION_HELP},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};
static void bsdar_usage(void);
static void ranlib_usage(void);
static void set_mode(struct bsdar *bsdar, char opt);
static void only_mode(struct bsdar *bsdar, const char *opt,
const char *valid_modes);
static void bsdar_version(void);
int
main(int argc, char **argv)
{
struct bsdar *bsdar, bsdar_storage;
char *arcmd, *argv1_saved;
size_t len;
int i, opt;
bsdar = &bsdar_storage;
memset(bsdar, 0, sizeof(*bsdar));
arcmd = argv1_saved = NULL;
bsdar->output = stdout;
if ((bsdar->progname = ELFTC_GETPROGNAME()) == NULL)
bsdar->progname = "ar";
if (elf_version(EV_CURRENT) == EV_NONE)
bsdar_errc(bsdar, 0, "ELF library initialization failed: %s",
elf_errmsg(-1));
/*
* Act like ranlib if our name ends in "ranlib"; this
* accomodates names like "arm-freebsd7.1-ranlib",
* "bsdranlib", etc.
*/
len = strlen(bsdar->progname);
if (len >= strlen("ranlib") &&
strcmp(bsdar->progname + len - strlen("ranlib"), "ranlib") == 0) {
while ((opt = getopt_long(argc, argv, "tDV", longopts,
NULL)) != -1) {
switch(opt) {
case 't':
/* Ignored. */
break;
case 'D':
bsdar->options |= AR_D;
break;
case 'V':
bsdar_version();
break;
case OPTION_HELP:
ranlib_usage();
default:
ranlib_usage();
}
}
argv += optind;
argc -= optind;
if (*argv == NULL)
ranlib_usage();
bsdar->options |= AR_S;
for (;(bsdar->filename = *argv++) != NULL;)
ar_write_archive(bsdar, 's');
exit(EXIT_SUCCESS);
} else {
if (argc < 2)
bsdar_usage();
/*
* Tack on a leading '-', for old-style usage.
*/
if (*argv[1] != '-') {
argv1_saved = argv[1];
len = strlen(argv[1]) + 2;
if ((arcmd = malloc(len)) == NULL)
bsdar_errc(bsdar, errno, "malloc failed");
(void) snprintf(arcmd, len, "-%s", argv[1]);
argv[1] = arcmd;
}
}
while ((opt = getopt_long(argc, argv, "abCcdDfF:ijlMmopqrSsTtuVvxz",
longopts, NULL)) != -1) {
switch(opt) {
case 'a':
bsdar->options |= AR_A;
break;
case 'b':
case 'i':
bsdar->options |= AR_B;
break;
case 'C':
bsdar->options |= AR_CC;
break;
case 'c':
bsdar->options |= AR_C;
break;
case 'd':
set_mode(bsdar, opt);
break;
case 'D':
bsdar->options |= AR_D;
break;
case 'F':
if (!strcasecmp(optarg, "svr4") ||
!strcasecmp(optarg, "gnu"))
bsdar->options &= ~AR_BSD;
else if (!strcasecmp(optarg, "bsd"))
bsdar->options |= AR_BSD;
else
bsdar_usage();
break;
case 'f':
case 'T':
bsdar->options |= AR_TR;
break;
case 'j':
/* ignored */
break;
case 'l':
/* ignored, for GNU ar comptibility */
break;
case 'M':
set_mode(bsdar, opt);
break;
case 'm':
set_mode(bsdar, opt);
break;
case 'o':
bsdar->options |= AR_O;
break;
case 'p':
set_mode(bsdar, opt);
break;
case 'q':
set_mode(bsdar, opt);
break;
case 'r':
set_mode(bsdar, opt);
break;
case 'S':
bsdar->options |= AR_SS;
break;
case 's':
bsdar->options |= AR_S;
break;
case 't':
set_mode(bsdar, opt);
break;
case 'u':
bsdar->options |= AR_U;
break;
case 'V':
bsdar_version();
break;
case 'v':
bsdar->options |= AR_V;
break;
case 'x':
set_mode(bsdar, opt);
break;
case 'z':
/* ignored */
break;
case OPTION_HELP:
bsdar_usage();
default:
bsdar_usage();
}
}
/* Restore argv[1] if we had modified it. */
if (arcmd != NULL) {
argv[1] = argv1_saved;
free(arcmd);
arcmd = argv1_saved = NULL;
}
argv += optind;
argc -= optind;
if (*argv == NULL && bsdar->mode != 'M')
bsdar_usage();
if (bsdar->options & AR_A && bsdar->options & AR_B)
bsdar_errc(bsdar, 0,
"only one of -a and -[bi] options allowed");
if (bsdar->options & AR_J && bsdar->options & AR_Z)
bsdar_errc(bsdar, 0,
"only one of -j and -z options allowed");
if (bsdar->options & AR_S && bsdar->options & AR_SS)
bsdar_errc(bsdar, 0,
"only one of -s and -S options allowed");
if (bsdar->options & (AR_A | AR_B)) {
if (*argv == NULL)
bsdar_errc(bsdar, 0,
"no position operand specified");
if ((bsdar->posarg = basename(*argv)) == NULL)
bsdar_errc(bsdar, errno,
"basename failed");
argc--;
argv++;
}
if (bsdar->options & AR_A)
only_mode(bsdar, "-a", "mqr");
if (bsdar->options & AR_B)
only_mode(bsdar, "-b", "mqr");
if (bsdar->options & AR_C)
only_mode(bsdar, "-c", "qr");
if (bsdar->options & AR_CC)
only_mode(bsdar, "-C", "x");
if (bsdar->options & AR_D)
only_mode(bsdar, "-D", "qr");
if (bsdar->options & AR_O)
only_mode(bsdar, "-o", "x");
if (bsdar->options & AR_SS)
only_mode(bsdar, "-S", "mqr");
if (bsdar->options & AR_U)
only_mode(bsdar, "-u", "qrx");
if (bsdar->mode == 'M') {
ar_mode_script(bsdar);
exit(EXIT_SUCCESS);
}
if ((bsdar->filename = *argv) == NULL)
bsdar_usage();
bsdar->argc = --argc;
bsdar->argv = ++argv;
if ((!bsdar->mode || strchr("ptx", bsdar->mode)) &&
bsdar->options & AR_S) {
ar_write_archive(bsdar, 's');
if (!bsdar->mode)
exit(EXIT_SUCCESS);
}
switch(bsdar->mode) {
case 'd': case 'm': case 'q': case 'r':
ar_write_archive(bsdar, bsdar->mode);
break;
case 'p': case 't': case 'x':
ar_read_archive(bsdar, bsdar->mode);
break;
default:
bsdar_usage();
/* NOTREACHED */
}
for (i = 0; i < bsdar->argc; i++)
if (bsdar->argv[i] != NULL)
bsdar_warnc(bsdar, 0, "%s: not found in archive",
bsdar->argv[i]);
exit(EXIT_SUCCESS);
}
static void
set_mode(struct bsdar *bsdar, char opt)
{
if (bsdar->mode != '\0' && bsdar->mode != opt)
bsdar_errc(bsdar, 0, "Can't specify both -%c and -%c",
opt, bsdar->mode);
bsdar->mode = opt;
}
static void
only_mode(struct bsdar *bsdar, const char *opt, const char *valid_modes)
{
if (strchr(valid_modes, bsdar->mode) == NULL)
bsdar_errc(bsdar, 0, "Option %s is not permitted in mode -%c",
opt, bsdar->mode);
}
#define AR_USAGE_MESSAGE "\
Usage: %s <command> [options] archive file...\n\
Manage archives.\n\n\
Where <command> is one of:\n\
-d Delete members from the archive.\n\
-m Move archive members within the archive.\n\
-p Write the contents of members to standard output.\n\
-q Append files to an archive.\n\
-r Replace (add) files to an archive.\n\
-s Add an archive symbol to an archive.\n\
-t List files in an archive.\n\
-x Extract members from an archive.\n\
-M Execute MRI librarian commands.\n\
-V Print a version identifier and exit.\n\n\
Options:\n\
-a MEMBER Add members after the specified member.\n\
-b MEMBER | -i MEMBER\n\
Add members before the specified member.\n\
-c Do not print a message when creating a new archive.\n\
-f | -T Only use the first fifteen characters of the member name.\n\
-j (This option is accepted, but is ignored).\n\
-l (This option is accepted, but is ignored).\n\
-o Preserve modification times when extracting members.\n\
-u Conditionally update or extract members.\n\
-v Be verbose.\n\
-z (This option is accepted, but is ignored).\n\
-C Do not overwrite existing files in the file system.\n\
-D Use fixed metadata, for consistent archive checksums.\n\
-F FORMAT | --flavor=FORMAT\n\
Create archives with the specified format.\n\
-S Do not generate an archive symbol table.\n"
static void
bsdar_usage(void)
{
(void) fprintf(stderr, AR_USAGE_MESSAGE, ELFTC_GETPROGNAME());
exit(EXIT_FAILURE);
}
#define RANLIB_USAGE_MESSAGE "\
Usage: %s [options] archive...\n\
Update or create archive symbol tables.\n\n\
Options:\n\
-t (This option is accepted, but ignored).\n\
-D Use fixed metadata, for consistent archive checksums.\n\
-V Print a version identifier and exit.\n"
static void
ranlib_usage(void)
{
(void)fprintf(stderr, RANLIB_USAGE_MESSAGE, ELFTC_GETPROGNAME());
exit(EXIT_FAILURE);
}
static void
bsdar_version(void)
{
(void)printf("%s (%s, %s)\n", ELFTC_GETPROGNAME(), archive_version_string(),
elftc_version());
exit(EXIT_SUCCESS);
}

143
ar/ar.h Normal file
View File

@ -0,0 +1,143 @@
/*-
* Copyright (c) 2007 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Id: ar.h 2496 2012-04-24 02:33:40Z jkoshy $
*/
#include <libelf.h>
#include "_elftc.h"
/*
* ar(1) options.
*/
#define AR_A 0x0001 /* position-after */
#define AR_B 0x0002 /* position-before */
#define AR_C 0x0004 /* creating new archive */
#define AR_CC 0x0008 /* do not overwrite when extracting */
#define AR_J 0x0010 /* bzip2 compression */
#define AR_O 0x0020 /* preserve original mtime when extracting */
#define AR_S 0x0040 /* write archive symbol table */
#define AR_SS 0x0080 /* do not write archive symbol table */
#define AR_TR 0x0100 /* only keep first 15 chars for member name */
#define AR_U 0x0200 /* only extract or update newer members.*/
#define AR_V 0x0400 /* verbose mode */
#define AR_Z 0x0800 /* gzip compression */
#define AR_D 0x1000 /* insert dummy mode, mtime, uid and gid */
#define AR_BSD 0x2000 /* use the BSD archive format */
#define DEF_BLKSZ 10240 /* default block size */
/* Special names. */
#define AR_STRINGTAB_NAME_SVR4 "//"
#define AR_SYMTAB_NAME_BSD "__.SYMDEF"
#define AR_SYMTAB_NAME_SVR4 "/"
/*
* Convenient wrapper for general libarchive error handling.
*/
#define AC(CALL) do { \
if ((CALL)) \
bsdar_errc(bsdar, 0, "%s", \
archive_error_string(a)); \
} while (0)
/*
* The 'ACV' wrapper is used for libarchive APIs that changed from
* returning 'void' to returning an 'int' in later versions of libarchive.
*/
#if ARCHIVE_VERSION_NUMBER >= 2000000
#define ACV(CALL) AC(CALL)
#else
#define ACV(CALL) do { \
(CALL); \
} while (0)
#endif
/*
* In-memory representation of archive member(object).
*/
struct ar_obj {
Elf *elf; /* object file descriptor */
char *name; /* member name */
uid_t uid; /* user id */
gid_t gid; /* group id */
mode_t md; /* octal file permissions */
size_t size; /* member size */
time_t mtime; /* modification time */
dev_t dev; /* inode's device */
ino_t ino; /* inode's number */
TAILQ_ENTRY(ar_obj) objs;
};
/*
* Structure encapsulates the "global" data for "ar" program.
*/
struct bsdar {
const char *filename; /* archive name. */
const char *addlib; /* target of ADDLIB. */
const char *posarg; /* position arg for modifiers -a, -b. */
char mode; /* program mode */
int options; /* command line options */
FILE *output; /* default output stream */
const char *progname; /* program name */
int argc;
char **argv;
dev_t ar_dev; /* archive device. */
ino_t ar_ino; /* archive inode. */
/*
* Fields for the archive string table.
*/
char *as; /* buffer for archive string table. */
size_t as_sz; /* current size of as table. */
size_t as_cap; /* capacity of as table buffer. */
/*
* Fields for the archive symbol table.
*/
uint32_t s_cnt; /* current number of symbols. */
uint32_t *s_so; /* symbol offset table. */
size_t s_so_cap; /* capacity of so table buffer. */
char *s_sn; /* symbol name table */
size_t s_sn_cap; /* capacity of sn table buffer. */
size_t s_sn_sz; /* current size of sn table. */
/* Current member's offset (relative to the end of pseudo members.) */
off_t rela_off;
TAILQ_HEAD(, ar_obj) v_obj; /* object(member) list */
};
void ar_mode_script(struct bsdar *ar);
void ar_read_archive(struct bsdar *_ar, int _mode);
void ar_write_archive(struct bsdar *_ar, int _mode);
void bsdar_errc(struct bsdar *, int _code, const char *fmt, ...);
int bsdar_is_pseudomember(struct bsdar *_ar, const char *_name);
const char *bsdar_strmode(mode_t m);
void bsdar_warnc(struct bsdar *, int _code, const char *fmt, ...);

65
ar/benchmark/acp.sh Executable file
View File

@ -0,0 +1,65 @@
#!/bin/sh
# $Id: acp.sh 2086 2011-10-27 05:18:01Z jkoshy $
# This script is adapted from Jan Psota's Tar Comparison Program(TCP).
n=3 # number of repetitions
AR="bsdar gnuar" # ar archivers to compare
test $# -ge 2 || {
echo "usage: $0 source_dir where_to_place_archive [where_to_extract_it]"
exit 0
}
THISDIR=`/bin/pwd`
src=$1
dst=$2/acp.a
ext=${3:-$2}/acptmp
test -e $dst -o -e /tmp/acp \
&& { echo "$dst or /tmp/acp exists, exiting"; exit 1; }
mkdir -p $ext || exit 1
show_result ()
{
awk -vL="`du -k $dst`" '{printf "%s\t%s\t%s\%10.1d KB/s\n",
$1, $3, $5, ($1>0?L/$1:0)}' /tmp/acp | sort | head -n 1
}
test -d $src || { echo "'$src' is not a directory"; exit 1; }
# ar versions
for ar in $AR; do echo -n "$ar: "; $ar -V | head -n 1;
done
echo
echo "best time of $n repetitions"
echo -n " src=$src, "
echo -n "`du -sh $src | awk '{print $1}'`"
echo -n " in "
echo "`find $src | wc -l` files"
echo " archive=$dst, extract to $ext"
echo "program operation real user system speed"
for op in "cru $dst $src/*" "t $dst" "x `basename $dst`"; do
for ar in $AR; do
echo -n "$ar "
echo $op | grep -q ^cr && echo -n "create "
echo $op | grep -q ^t && echo -n "list "
echo $op | grep -q ^x && echo -n "extract "
num=0
while [ $num -lt $n ]; do
echo $op | grep -q ^cr && rm -f $dst
echo $op | grep -q ^x && { rm -rf $ext; mkdir -p $ext
cp $dst $ext; cd $ext; }
sync
time $ar $op > /dev/null 2>> /tmp/acp
echo $op | grep -q ^x && cd $THISDIR
num=`expr $num + 1`
done
show_result
rm -rf /tmp/acp
done
echo
done
rm -rf $ext $dst
rm -f /tmp/acp

86
ar/ranlib.1 Normal file
View File

@ -0,0 +1,86 @@
.\" Copyright (c) 2007,2009-2012 Joseph Koshy. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" This software is provided by Joseph Koshy ``as is'' and
.\" any express or implied warranties, including, but not limited to, the
.\" implied warranties of merchantability and fitness for a particular purpose
.\" are disclaimed. in no event shall Joseph Koshy be liable
.\" for any direct, indirect, incidental, special, exemplary, or consequential
.\" damages (including, but not limited to, procurement of substitute goods
.\" or services; loss of use, data, or profits; or business interruption)
.\" however caused and on any theory of liability, whether in contract, strict
.\" liability, or tort (including negligence or otherwise) arising in any way
.\" out of the use of this software, even if advised of the possibility of
.\" such damage.
.\"
.\" $Id: ranlib.1 2739 2012-12-09 17:07:46Z jkoshy $
.\"
.Dd December 9, 2012
.Os
.Dt RANLIB 1
.Sh NAME
.Nm ranlib
.Nd update archive symbol tables
.Sh SYNOPSIS
.Nm
.Op Fl D
.Op Fl t
.Ar archive Ns ...
.Nm
.Fl V
.Sh DESCRIPTION
The
.Nm ranlib
utility is used to update an existing archive symbol table in an
.Xr ar 1
archive, or to add an archive symbol table to an archive lacking one.
.Sh OPTIONS
The
.Nm
utility supports the following options:
.Bl -tag -width indent
.It Fl D
Use zeros for the mtime, uid and gid fields, and use mode 0644 for the
file mode field for all archive member headers.
This ensures that checksums on the resulting archives are reproducible
when member contents are identical.
.It Fl t
This option is accepted, but is ignored.
.It Fl V
Print a version identifier and exit.
.El
.Sh EXAMPLES
To update the archive symbol table for an archive
.Pa lib.a ,
use:
.Dl "ranlib lib.a"
.Sh DIAGNOSTICS
.Ex -std
.Sh SEE ALSO
.Xr ar 1 ,
.Xr ld 1 ,
.Xr archive 3 ,
.Xr elf 3 ,
.Xr ar 5
.Sh HISTORY
The
.Nm
command first appeared in AT&T UNIX Version 7.
.Pp
In
.Fx 8.0 ,
.An "Kai Wang" Aq kaiw@FreeBSD.org
reimplemented
.Nm
using the
.Lb libarchive
and the
.Lb libelf .

192
ar/read.c Normal file
View File

@ -0,0 +1,192 @@
/*-
* Copyright (c) 2007 Kai Wang
* Copyright (c) 2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <archive.h>
#include <archive_entry.h>
#include <assert.h>
#include <errno.h>
#include <libgen.h>
#include <stdio.h>
#include <string.h>
#include "ar.h"
ELFTC_VCSID("$Id: read.c 2229 2011-11-27 13:25:37Z jkoshy $");
/*
* Handle read modes: 'x', 't' and 'p'.
*/
void
ar_read_archive(struct bsdar *bsdar, int mode)
{
FILE *out;
struct archive *a;
struct archive_entry *entry;
struct stat sb;
struct tm *tp;
const char *bname;
const char *name;
mode_t md;
size_t size;
time_t mtime;
uid_t uid;
gid_t gid;
char **av;
char buf[25];
char find;
int i, flags, r;
assert(mode == 'p' || mode == 't' || mode == 'x');
if ((a = archive_read_new()) == NULL)
bsdar_errc(bsdar, 0, "archive_read_new failed");
archive_read_support_compression_none(a);
archive_read_support_format_ar(a);
AC(archive_read_open_file(a, bsdar->filename, DEF_BLKSZ));
out = bsdar->output;
for (;;) {
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY ||
r == ARCHIVE_FATAL)
bsdar_warnc(bsdar, 0, "%s", archive_error_string(a));
if (r == ARCHIVE_EOF || r == ARCHIVE_FATAL)
break;
if (r == ARCHIVE_RETRY) {
bsdar_warnc(bsdar, 0, "Retrying...");
continue;
}
if (archive_format(a) == ARCHIVE_FORMAT_AR_BSD)
bsdar->options |= AR_BSD;
else
bsdar->options &= ~AR_BSD;
name = archive_entry_pathname(entry);
/* Skip pseudo members. */
if (bsdar_is_pseudomember(bsdar, name))
continue;
if (bsdar->argc > 0) {
find = 0;
for(i = 0; i < bsdar->argc; i++) {
av = &bsdar->argv[i];
if (*av == NULL)
continue;
if ((bname = basename(*av)) == NULL)
bsdar_errc(bsdar, errno,
"basename failed");
if (strcmp(bname, name) != 0)
continue;
*av = NULL;
find = 1;
break;
}
if (!find)
continue;
}
if (mode == 't') {
if (bsdar->options & AR_V) {
md = archive_entry_mode(entry);
uid = archive_entry_uid(entry);
gid = archive_entry_gid(entry);
size = archive_entry_size(entry);
mtime = archive_entry_mtime(entry);
(void)fprintf(out, "%s %6d/%-6d %8ju ",
bsdar_strmode(md) + 1, uid, gid,
(uintmax_t)size);
tp = localtime(&mtime);
(void)strftime(buf, sizeof(buf),
"%b %e %H:%M %Y", tp);
(void)fprintf(out, "%s %s", buf, name);
} else
(void)fprintf(out, "%s", name);
r = archive_read_data_skip(a);
if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY ||
r == ARCHIVE_FATAL) {
(void)fprintf(out, "\n");
bsdar_warnc(bsdar, 0, "%s",
archive_error_string(a));
}
if (r == ARCHIVE_FATAL)
break;
(void)fprintf(out, "\n");
} else {
/* mode == 'x' || mode = 'p' */
if (mode == 'p') {
if (bsdar->options & AR_V) {
(void)fprintf(out, "\n<%s>\n\n",
name);
fflush(out);
}
r = archive_read_data_into_fd(a, fileno(out));
} else {
/* mode == 'x' */
if (stat(name, &sb) != 0) {
if (errno != ENOENT) {
bsdar_warnc(bsdar, 0,
"stat %s failed",
bsdar->filename);
continue;
}
} else {
/* stat success, file exist */
if (bsdar->options & AR_CC)
continue;
if (bsdar->options & AR_U &&
archive_entry_mtime(entry) <=
sb.st_mtime)
continue;
}
if (bsdar->options & AR_V)
(void)fprintf(out, "x - %s\n", name);
flags = 0;
if (bsdar->options & AR_O)
flags |= ARCHIVE_EXTRACT_TIME;
r = archive_read_extract(a, entry, flags);
}
if (r)
bsdar_warnc(bsdar, 0, "%s",
archive_error_string(a));
}
}
AC(archive_read_close(a));
ACV(archive_read_finish(a));
}

185
ar/util.c Normal file
View File

@ -0,0 +1,185 @@
/*-
* Copyright (c) 2003-2007 Tim Kientzle
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include "ar.h"
ELFTC_VCSID("$Id: util.c 2130 2011-11-10 06:56:46Z jkoshy $");
static void bsdar_vwarnc(struct bsdar *, int code,
const char *fmt, va_list ap);
static void bsdar_verrc(struct bsdar *bsdar, int code,
const char *fmt, va_list ap);
static void
bsdar_vwarnc(struct bsdar *bsdar, int code, const char *fmt, va_list ap)
{
fprintf(stderr, "%s: warning: ", bsdar->progname);
vfprintf(stderr, fmt, ap);
if (code != 0)
fprintf(stderr, ": %s", strerror(code));
fprintf(stderr, "\n");
}
void
bsdar_warnc(struct bsdar *bsdar, int code, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
bsdar_vwarnc(bsdar, code, fmt, ap);
va_end(ap);
}
static void
bsdar_verrc(struct bsdar *bsdar, int code, const char *fmt, va_list ap)
{
fprintf(stderr, "%s: fatal: ", bsdar->progname);
vfprintf(stderr, fmt, ap);
if (code != 0)
fprintf(stderr, ": %s", strerror(code));
fprintf(stderr, "\n");
}
void
bsdar_errc(struct bsdar *bsdar, int code, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
bsdar_verrc(bsdar, code, fmt, ap);
va_end(ap);
exit(EXIT_FAILURE);
}
#define AR_STRMODE_SIZE 12
const char *
bsdar_strmode(mode_t m)
{
static char buf[AR_STRMODE_SIZE];
#if ELFTC_HAVE_STRMODE
/* Use the system's strmode(3). */
strmode(m, buf);
return buf;
#else
char c;
/*
* The first character of the string denotes the type of the
* entry.
*/
if (S_ISBLK(m))
c = 'b';
else if (S_ISCHR(m))
c = 'c';
else if (S_ISDIR(m))
c = 'd';
#if defined(S_ISFIFO)
else if (S_ISFIFO(m))
c = 'p';
#endif
#if defined(S_ISLNK)
else if (S_ISLNK(m))
c = 'l';
#endif
else if (S_ISREG(m))
c = '-';
#if defined(S_ISSOCK)
else if (S_ISSOCK(m))
c = 's';
#endif
else
c = '?';
buf[0] = c;
/* The next 3 characters show permissions for the owner. */
buf[1] = (m & S_IRUSR) ? 'r' : '-';
buf[2] = m & S_IWUSR ? 'w' : '-';
if (m & S_ISUID)
c = (m & S_IXUSR) ? 's' : 'S';
else
c = (m & S_IXUSR) ? 'x' : '-';
buf[3] = c;
/* The next 3 characters describe permissions for the group. */
buf[4] = (m & S_IRGRP) ? 'r' : '-';
buf[5] = m & S_IWGRP ? 'w' : '-';
if (m & S_ISGID)
c = (m & S_IXGRP) ? 's' : 'S';
else
c = (m & S_IXGRP) ? 'x' : '-';
buf[6] = c;
/* The next 3 characters describe permissions for others. */
buf[7] = (m & S_IROTH) ? 'r' : '-';
buf[8] = m & S_IWOTH ? 'w' : '-';
if (m & S_ISVTX) /* sticky bit */
c = (m & S_IXOTH) ? 't' : 'T';
else
c = (m & S_IXOTH) ? 'x' : '-';
buf[9] = c;
/* End the string with a blank and NUL-termination. */
buf[10] = ' ';
buf[11] = '\0';
return buf;
#endif /* !ELTC_HAVE_STRMODE */
}
int
bsdar_is_pseudomember(struct bsdar *bsdar, const char *name)
{
/*
* The "__.SYMDEF" member is special in the BSD format
* variant.
*/
if (bsdar->options & AR_BSD)
return (strcmp(name, AR_SYMTAB_NAME_BSD) == 0);
else
/*
* The names "/ " and "// " are special in the SVR4
* variant.
*/
return (strcmp(name, AR_STRINGTAB_NAME_SVR4) == 0 ||
strcmp(name, AR_SYMTAB_NAME_SVR4) == 0);
}

978
ar/write.c Normal file
View File

@ -0,0 +1,978 @@
/*-
* Copyright (c) 2007 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <archive.h>
#include <archive_entry.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <gelf.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ar.h"
ELFTC_VCSID("$Id: write.c 2496 2012-04-24 02:33:40Z jkoshy $");
#define _ARMAG_LEN 8 /* length of the magic string */
#define _ARHDR_LEN 60 /* length of the archive header */
#define _INIT_AS_CAP 128 /* initial archive string table size */
#define _INIT_SYMOFF_CAP (256*(sizeof(uint32_t))) /* initial so table size */
#define _INIT_SYMNAME_CAP 1024 /* initial sn table size */
#define _MAXNAMELEN_SVR4 15 /* max member name length in svr4 variant */
#define _MAXNAMELEN_BSD 16 /* max member name length in bsd variant */
#define _TRUNCATE_LEN 15 /* number of bytes to keep for member name */
static void add_to_ar_str_table(struct bsdar *bsdar, const char *name);
static void add_to_ar_sym_table(struct bsdar *bsdar, const char *name);
static struct ar_obj *create_obj_from_file(struct bsdar *bsdar,
const char *name, time_t mtime);
static void create_symtab_entry(struct bsdar *bsdar, Elf *e);
static void free_obj(struct ar_obj *obj);
static void insert_obj(struct bsdar *bsdar, struct ar_obj *obj,
struct ar_obj *pos);
static void read_objs(struct bsdar *bsdar, const char *archive,
int checkargv);
static void write_cleanup(struct bsdar *bsdar);
static void write_data(struct bsdar *bsdar, struct archive *a,
const void *buf, size_t s);
static void write_objs(struct bsdar *bsdar);
/*
* Create an object from a file, and return the created object
* descriptor. Return NULL if either an error occurs, or if the '-u'
* option was specifed and the member is not newer than the existing
* one in the archive.
*/
static struct ar_obj *
create_obj_from_file(struct bsdar *bsdar, const char *name, time_t mtime)
{
struct ar_obj *obj;
struct stat sb;
const char *bname;
char *tmpname;
int fd;
if (name == NULL)
return (NULL);
obj = malloc(sizeof(struct ar_obj));
if (obj == NULL)
bsdar_errc(bsdar, errno, "malloc failed");
obj->elf = NULL;
if ((fd = open(name, O_RDONLY, 0)) < 0) {
bsdar_warnc(bsdar, errno, "can't open file: %s", name);
free(obj);
return (NULL);
}
tmpname = strdup(name);
if ((bname = basename(tmpname)) == NULL)
bsdar_errc(bsdar, errno, "basename failed");
if (bsdar->options & AR_TR && strlen(bname) > _TRUNCATE_LEN) {
if ((obj->name = malloc(_TRUNCATE_LEN + 1)) == NULL)
bsdar_errc(bsdar, errno, "malloc failed");
(void)strncpy(obj->name, bname, _TRUNCATE_LEN);
obj->name[_TRUNCATE_LEN] = '\0';
} else
if ((obj->name = strdup(bname)) == NULL)
bsdar_errc(bsdar, errno, "strdup failed");
free(tmpname);
if (fstat(fd, &sb) < 0) {
bsdar_warnc(bsdar, errno, "can't fstat file: %s", obj->name);
goto giveup;
}
if (!S_ISREG(sb.st_mode)) {
bsdar_warnc(bsdar, 0, "%s is not an ordinary file", obj->name);
goto giveup;
}
if (sb.st_dev == bsdar->ar_dev && sb.st_ino == bsdar->ar_ino) {
bsdar_warnc(bsdar, 0, "cannot add archive \"%s\" to itself",
obj->name);
goto giveup;
}
/*
* If the '-u' option is specified and member is not newer
* than the existing one, we should not replace the member.
* However, if mtime == 0, i.e., if nonexistent members are to
* be forcibly replaced, then the '-u' option is to be ignored.
*/
if (mtime != 0 && bsdar->options & AR_U && sb.st_mtime <= mtime)
goto giveup;
/*
* When the '-D' option is specified, the mtime and UID/GID of
* the member will be set to 0, and the file mode will be set
* to 644. This ensures that checksums will match for two
* archives containing identical content.
*/
if (bsdar->options & AR_D) {
obj->uid = 0;
obj->gid = 0;
obj->mtime = 0;
obj->md = S_IFREG | 0644;
} else {
obj->uid = sb.st_uid;
obj->gid = sb.st_gid;
obj->mtime = sb.st_mtime;
obj->md = sb.st_mode;
}
obj->size = sb.st_size;
obj->dev = sb.st_dev;
obj->ino = sb.st_ino;
if (obj->size == 0) {
return (obj);
}
if ((obj->elf = elf_open(fd)) == NULL) {
bsdar_warnc(bsdar, 0, "file initialization failed for %s: %s",
obj->name, elf_errmsg(-1));
goto giveup;
}
/*
* Read the object fully into memory and close its file
* descriptor.
*/
if (elf_cntl(obj->elf, ELF_C_FDREAD) < 0) {
bsdar_warnc(bsdar, 0, "%s could not be read in: %s",
obj->name, elf_errmsg(-1));
goto giveup;
}
if (close(fd) < 0)
bsdar_errc(bsdar, errno, "close failed: %s",
obj->name);
return (obj);
giveup:
if (obj->elf)
elf_end(obj->elf);
if (close(fd) < 0)
bsdar_errc(bsdar, errno, "close failed: %s",
obj->name);
free(obj->name);
free(obj);
return (NULL);
}
/*
* Free an object and its associated allocations.
*/
static void
free_obj(struct ar_obj *obj)
{
if (obj->elf)
elf_end(obj->elf);
free(obj->name);
free(obj);
}
/*
* Insert an object into a list, either before/after the 'pos' obj or
* at the end of the list.
*/
static void
insert_obj(struct bsdar *bsdar, struct ar_obj *obj, struct ar_obj *pos)
{
if (obj == NULL)
bsdar_errc(bsdar, 0, "try to insert a null obj");
if (pos == NULL || obj == pos)
/*
* If the object to move happens to be the position
* obj, or if there is no position obj, move the
* object to the end.
*/
goto tail;
if (bsdar->options & AR_B) {
TAILQ_INSERT_BEFORE(pos, obj, objs);
return;
}
if (bsdar->options & AR_A) {
TAILQ_INSERT_AFTER(&bsdar->v_obj, pos, obj, objs);
return;
}
tail:
TAILQ_INSERT_TAIL(&bsdar->v_obj, obj, objs);
}
/*
* Read objects from archive into the 'v_obj' list. Note that
* 'checkargv' is set when read_objs() is used to read objects from
* the target of 'ADDLIB' command in ar script mode; in this case the
* 'argv' array specifies the members that 'ADDLIB' is to operate on.
*/
static void
read_objs(struct bsdar *bsdar, const char *archive, int checkargv)
{
struct archive *a;
struct archive_entry *entry;
struct ar_obj *obj;
const char *name;
const char *bname;
char *buff;
char **av;
size_t size;
int i, r, find;
if ((a = archive_read_new()) == NULL)
bsdar_errc(bsdar, 0, "archive_read_new failed");
archive_read_support_compression_none(a);
archive_read_support_format_ar(a);
AC(archive_read_open_filename(a, archive, DEF_BLKSZ));
for (;;) {
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_FATAL)
bsdar_errc(bsdar, 0, "%s", archive_error_string(a));
if (r == ARCHIVE_EOF)
break;
if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY)
bsdar_warnc(bsdar, 0, "%s", archive_error_string(a));
if (r == ARCHIVE_RETRY) {
bsdar_warnc(bsdar, 0, "Retrying...");
continue;
}
name = archive_entry_pathname(entry);
/*
* Skip pseudo members.
*/
if (bsdar_is_pseudomember(bsdar, name))
continue;
/*
* If 'checkargv' is set, only read those members
* specified in argv.
*/
if (checkargv && bsdar->argc > 0) {
find = 0;
for(i = 0; i < bsdar->argc; i++) {
av = &bsdar->argv[i];
if (*av == NULL)
continue;
if ((bname = basename(*av)) == NULL)
bsdar_errc(bsdar, errno,
"basename failed");
if (strcmp(bname, name) != 0)
continue;
*av = NULL;
find = 1;
break;
}
if (!find)
continue;
}
size = archive_entry_size(entry);
if (size > 0) {
if ((buff = malloc(size)) == NULL)
bsdar_errc(bsdar, errno, "malloc failed");
if (archive_read_data(a, buff, size) != (ssize_t)size) {
bsdar_warnc(bsdar, 0, "%s",
archive_error_string(a));
free(buff);
continue;
}
} else
buff = NULL;
obj = malloc(sizeof(struct ar_obj));
if (obj == NULL)
bsdar_errc(bsdar, errno, "malloc failed");
obj->elf = NULL;
if (buff) {
obj->elf = elf_openmemory(buff, size);
if (obj->elf == NULL) {
bsdar_warnc(bsdar, 0, "elf_openmemory() "
"failed for %s: %s", name,
elf_errmsg(-1));
free(buff);
free(obj);
continue;
}
}
if ((obj->name = strdup(name)) == NULL)
bsdar_errc(bsdar, errno, "strdup failed");
obj->size = size;
obj->uid = archive_entry_uid(entry);
obj->gid = archive_entry_gid(entry);
obj->md = archive_entry_mode(entry);
obj->mtime = archive_entry_mtime(entry);
obj->dev = 0;
obj->ino = 0;
TAILQ_INSERT_TAIL(&bsdar->v_obj, obj, objs);
}
AC(archive_read_close(a));
ACV(archive_read_finish(a));
}
/*
* Write an archive.
*/
void
ar_write_archive(struct bsdar *bsdar, int mode)
{
struct ar_obj *nobj, *obj, *obj_temp, *pos;
struct stat sb;
const char *bname;
char **av;
int i;
TAILQ_INIT(&bsdar->v_obj);
nobj = NULL;
pos = NULL;
memset(&sb, 0, sizeof(sb));
assert(mode == 'A' || mode == 'd' || mode == 'm' || mode == 'q' ||
mode == 'r' || mode == 's');
/*
* Test if the specified archive exists, to determine
* whether we are creating a new archive.
*/
if (stat(bsdar->filename, &sb) != 0) {
if (errno != ENOENT) {
bsdar_warnc(bsdar, 0, "stat %s failed",
bsdar->filename);
return;
}
/* We do not create archive in mode 'd', 'm' and 's'. */
if (mode != 'r' && mode != 'q') {
bsdar_warnc(bsdar, 0, "%s: no such file",
bsdar->filename);
return;
}
/* Issue a message if the '-c' option was not specified. */
if (!(bsdar->options & AR_C))
bsdar_warnc(bsdar, 0, "creating %s", bsdar->filename);
goto new_archive;
}
bsdar->ar_dev = sb.st_dev;
bsdar->ar_ino = sb.st_ino;
/*
* First read members from the existing archive.
*/
read_objs(bsdar, bsdar->filename, 0);
/*
* For mode 's', no member will be moved, deleted or replaced.
*/
if (mode == 's')
goto write_objs;
/*
* For mode 'q', we don't need to adjust existing members either.
* Also, -a, -b and -i are ignored in this mode. New members are
* always inserted at tail.
*/
if (mode == 'q')
goto new_archive;
/*
* Mode 'A' adds the contents of another archive to the tail
* of current archive. Note that mode 'A' is a special mode
* for the 'ADDLIB' command in ar's script mode. Currently
* there is no option that invokes this function from ar's
* command line.
*/
if (mode == 'A') {
/*
* Read objects from the target archive of the
* 'ADDLIB' command. If there are members spcified in
* 'argv', read those members only, otherwise the
* entire archive will be read.
*/
read_objs(bsdar, bsdar->addlib, 1);
goto write_objs;
}
/*
* Try to find the position member specified by user.
*/
if (bsdar->options & AR_A || bsdar->options & AR_B) {
TAILQ_FOREACH(obj, &bsdar->v_obj, objs) {
if (strcmp(obj->name, bsdar->posarg) == 0) {
pos = obj;
break;
}
}
/*
* If we cannot find the position specified by the
* user, sliently insert objects at the tail of the
* list.
*/
if (pos == NULL)
bsdar->options &= ~(AR_A | AR_B);
}
for (i = 0; i < bsdar->argc; i++) {
av = &bsdar->argv[i];
TAILQ_FOREACH_SAFE(obj, &bsdar->v_obj, objs, obj_temp) {
if ((bname = basename(*av)) == NULL)
bsdar_errc(bsdar, errno, "basename failed");
if (bsdar->options & AR_TR) {
if (strncmp(bname, obj->name, _TRUNCATE_LEN))
continue;
} else
if (strcmp(bname, obj->name) != 0)
continue;
if (mode == 'r') {
/*
* If the new member should not
* replace the old one, skip it.
*/
nobj = create_obj_from_file(bsdar, *av,
obj->mtime);
if (nobj == NULL)
goto skip_obj;
}
if (bsdar->options & AR_V)
(void)fprintf(bsdar->output, "%c - %s\n",
mode, *av);
TAILQ_REMOVE(&bsdar->v_obj, obj, objs);
if (mode == 'd' || mode == 'r')
free_obj(obj);
if (mode == 'm')
insert_obj(bsdar, obj, pos);
if (mode == 'r')
insert_obj(bsdar, nobj, pos);
skip_obj:
*av = NULL;
break;
}
}
new_archive:
/*
* When operating in mode 'r', directly add the specified
* objects which do not exist in current archive. When
* operating in mode 'q', all objects specified by the command
* line args are appended to the archive, without checking
* existing members in the archive.
*/
for (i = 0; i < bsdar->argc; i++) {
av = &bsdar->argv[i];
if (*av != NULL && (mode == 'r' || mode == 'q')) {
nobj = create_obj_from_file(bsdar, *av, 0);
if (nobj != NULL)
insert_obj(bsdar, nobj, pos);
if (bsdar->options & AR_V && nobj != NULL)
(void)fprintf(bsdar->output, "a - %s\n", *av);
*av = NULL;
}
}
write_objs:
write_objs(bsdar);
write_cleanup(bsdar);
}
/*
* Release memory.
*/
static void
write_cleanup(struct bsdar *bsdar)
{
struct ar_obj *obj, *obj_temp;
TAILQ_FOREACH_SAFE(obj, &bsdar->v_obj, objs, obj_temp) {
TAILQ_REMOVE(&bsdar->v_obj, obj, objs);
free_obj(obj);
}
free(bsdar->as);
free(bsdar->s_so);
free(bsdar->s_sn);
bsdar->as = NULL;
bsdar->s_so = NULL;
bsdar->s_sn = NULL;
}
/*
* Wrapper for archive_write_data().
*/
static void
write_data(struct bsdar *bsdar, struct archive *a, const void *buf, size_t s)
{
if (archive_write_data(a, buf, s) != (ssize_t)s)
bsdar_errc(bsdar, 0, "%s", archive_error_string(a));
}
/*
* Compute the size of the symbol table for an archive.
*/
static size_t
bsdar_symtab_size(struct bsdar *bsdar)
{
size_t sz;
if (bsdar->options & AR_BSD) {
/*
* A BSD style symbol table has two parts.
* Each part is preceded by its size in bytes,
* encoded as a C 'long'. In the first part,
* there are 's_cnt' entries, each entry being
* 2 'long's in size. The second part
* contains a string table.
*/
sz = 2 * sizeof(long) + (bsdar->s_cnt * 2 * sizeof(long)) +
bsdar->s_sn_sz;
} else {
/*
* An SVR4 style symbol table comprises of a 32 bit
* number holding the number of entries, followed by
* that many 32-bit offsets, followed by a string
* table.
*/
sz = sizeof(uint32_t) + bsdar->s_cnt * sizeof(uint32_t) +
bsdar->s_sn_sz;
}
return (sz);
}
static void
write_svr4_symtab_entry(struct bsdar *bsdar, struct archive *a)
{
int nr;
uint32_t i;
/* Translate offsets to big-endian form. */
for (i = 0; i < bsdar->s_cnt; i++)
bsdar->s_so[i] = htobe32(bsdar->s_so[i]);
nr = htobe32(bsdar->s_cnt);
write_data(bsdar, a, &nr, sizeof(uint32_t));
write_data(bsdar, a, bsdar->s_so, sizeof(uint32_t) *
bsdar->s_cnt);
write_data(bsdar, a, bsdar->s_sn, bsdar->s_sn_sz);
}
static void
write_bsd_symtab_entry(struct bsdar *bsdar, struct archive *a)
{
long br_sz, br_off, br_strx;
char *s;
uint32_t i;
/*
* Write out the size in the byte of the array of 'ranlib'
* descriptors to follow.
*/
br_sz = (long) (bsdar->s_cnt * 2 * sizeof(long));
write_data(bsdar, a, &br_sz, sizeof(long));
/*
* Write out the array of 'ranlib' descriptors. Each
* descriptor comprises of (a) an offset into the following
* string table and (b) a file offset to the relevant member.
*/
for (i = 0, s = bsdar->s_sn; i < bsdar->s_cnt; i++) {
br_strx = (long) (s - bsdar->s_sn);
br_off = (long) bsdar->s_so[i];
write_data(bsdar, a, &br_strx, sizeof(long));
write_data(bsdar, a, &br_off, sizeof(long));
/* Find the start of the next symbol in the string table. */
while (*s++ != '\0')
;
}
/*
* Write out the size of the string table as a 'long',
* followed by the string table itself.
*/
br_sz = (long) bsdar->s_sn_sz;
write_data(bsdar, a, &br_sz, sizeof(long));
write_data(bsdar, a, bsdar->s_sn, bsdar->s_sn_sz);
}
/*
* Write the resulting archive members.
*/
static void
write_objs(struct bsdar *bsdar)
{
struct ar_obj *obj;
struct archive *a;
struct archive_entry *entry;
size_t s_sz; /* size of archive symbol table. */
size_t pm_sz; /* size of pseudo members */
size_t namelen; /* size of member name. */
size_t obj_sz; /* size of object + extended header. */
int i;
char *buf;
const char *entry_name;
bsdar->rela_off = 0;
/*
* Create the archive symbol table and the archive string
* table, if needed.
*/
TAILQ_FOREACH(obj, &bsdar->v_obj, objs) {
if (!(bsdar->options & AR_SS) && obj->elf != NULL)
create_symtab_entry(bsdar, obj->elf);
obj_sz = 0;
namelen = strlen(obj->name);
if (bsdar->options & AR_BSD) {
/* Account for the space used by the file name. */
if (namelen > _MAXNAMELEN_BSD ||
strchr(obj->name, ' '))
obj_sz += namelen;
} else if (namelen > _MAXNAMELEN_SVR4)
add_to_ar_str_table(bsdar, obj->name);
obj_sz += obj->size; /* add the actual object size */
/* Roundup the final size and add the header length. */
bsdar->rela_off += _ARHDR_LEN + obj_sz + (obj_sz & 1);
}
/*
* Pad the symbol name string table. It is treated specially
* because symbol name table should be padded by a '\0', and
* not '\n' as for normal members. The size of the 'sn' table
* includes the pad byte.
*/
if (bsdar->s_cnt != 0 && bsdar->s_sn_sz % 2 != 0)
bsdar->s_sn[bsdar->s_sn_sz++] = '\0';
/*
* The archive string table is padded by a "\n" like a normal
* member. The difference is that the size of archive string
* table includes the pad byte, while normal members' size
* fields do not.
*/
if (bsdar->as != NULL && bsdar->as_sz % 2 != 0)
bsdar->as[bsdar->as_sz++] = '\n';
/*
* If there is a symbol table, calculate the size of pseudo
* members, and convert previously stored relative offsets to
* absolute ones.
*
* absolute_offset = relative_offset + size_of_pseudo_members)
*/
s_sz = bsdar_symtab_size(bsdar);
if (bsdar->s_cnt != 0) {
pm_sz = _ARMAG_LEN + (_ARHDR_LEN + s_sz);
if (bsdar->as != NULL) /* SVR4 archives only */
pm_sz += _ARHDR_LEN + bsdar->as_sz;
for (i = 0; (size_t) i < bsdar->s_cnt; i++)
bsdar->s_so[i] = bsdar->s_so[i] + pm_sz;
}
if ((a = archive_write_new()) == NULL)
bsdar_errc(bsdar, 0, "archive_write_new failed");
if (bsdar->options & AR_BSD)
archive_write_set_format_ar_bsd(a);
else
archive_write_set_format_ar_svr4(a);
archive_write_set_compression_none(a);
AC(archive_write_open_filename(a, bsdar->filename));
/*
* Write the archive symbol table, if there is one. If
* options '-s' was explicitly specified or if we were invoked
* as 'ranlib', write the symbol table even if it is empty.
*/
if ((bsdar->s_cnt != 0 && !(bsdar->options & AR_SS)) ||
bsdar->options & AR_S) {
if (bsdar->options & AR_BSD)
entry_name = AR_SYMTAB_NAME_BSD;
else
entry_name = AR_SYMTAB_NAME_SVR4;
entry = archive_entry_new();
archive_entry_copy_pathname(entry, entry_name);
if ((bsdar->options & AR_D) == 0)
archive_entry_set_mtime(entry, time(NULL), 0);
archive_entry_set_size(entry, s_sz);
AC(archive_write_header(a, entry));
if (bsdar->options & AR_BSD)
write_bsd_symtab_entry(bsdar, a);
else
write_svr4_symtab_entry(bsdar, a);
archive_entry_free(entry);
}
/* Write the archive string table, if any. */
if (bsdar->as != NULL) {
entry = archive_entry_new();
archive_entry_copy_pathname(entry, AR_STRINGTAB_NAME_SVR4);
archive_entry_set_size(entry, bsdar->as_sz);
AC(archive_write_header(a, entry));
write_data(bsdar, a, bsdar->as, bsdar->as_sz);
archive_entry_free(entry);
}
/* Write normal members. */
TAILQ_FOREACH(obj, &bsdar->v_obj, objs) {
if ((buf = elf_rawfile(obj->elf, NULL)) == NULL) {
bsdar_warnc(bsdar, 0, "elf_rawfile() failed: %s",
elf_errmsg(-1));
continue;
}
entry = archive_entry_new();
archive_entry_copy_pathname(entry, obj->name);
archive_entry_set_uid(entry, obj->uid);
archive_entry_set_gid(entry, obj->gid);
archive_entry_set_mode(entry, obj->md);
archive_entry_set_size(entry, obj->size);
archive_entry_set_mtime(entry, obj->mtime, 0);
archive_entry_set_dev(entry, obj->dev);
archive_entry_set_ino(entry, obj->ino);
archive_entry_set_filetype(entry, AE_IFREG);
AC(archive_write_header(a, entry));
write_data(bsdar, a, buf, obj->size);
archive_entry_free(entry);
}
AC(archive_write_close(a));
ACV(archive_write_finish(a));
}
/*
* Extract global symbols from ELF binary members.
*/
static void
create_symtab_entry(struct bsdar *bsdar, Elf *e)
{
Elf_Scn *scn;
GElf_Shdr shdr;
GElf_Sym sym;
Elf_Data *data;
char *name;
size_t n, shstrndx;
int elferr, tabndx, len, i;
if (elf_kind(e) != ELF_K_ELF) {
/* Silently a ignore non-ELF member. */
return;
}
if (elf_getshstrndx(e, &shstrndx) == 0) {
bsdar_warnc(bsdar, 0, "elf_getshstrndx failed: %s",
elf_errmsg(-1));
return;
}
tabndx = -1;
scn = NULL;
while ((scn = elf_nextscn(e, scn)) != NULL) {
if (gelf_getshdr(scn, &shdr) != &shdr) {
bsdar_warnc(bsdar, 0,
"elf_getshdr failed: %s", elf_errmsg(-1));
continue;
}
if ((name = elf_strptr(e, shstrndx, shdr.sh_name)) == NULL) {
bsdar_warnc(bsdar, 0,
"elf_strptr failed: %s", elf_errmsg(-1));
continue;
}
if (strcmp(name, ".strtab") == 0) {
tabndx = elf_ndxscn(scn);
break;
}
}
elferr = elf_errno();
if (elferr != 0)
bsdar_warnc(bsdar, 0, "elf_nextscn failed: %s",
elf_errmsg(elferr));
if (tabndx == -1) {
bsdar_warnc(bsdar, 0, "can't find .strtab section");
return;
}
scn = NULL;
while ((scn = elf_nextscn(e, scn)) != NULL) {
if (gelf_getshdr(scn, &shdr) != &shdr) {
bsdar_warnc(bsdar, 0, "elf_getshdr failed: %s",
elf_errmsg(-1));
continue;
}
if (shdr.sh_type != SHT_SYMTAB)
continue;
data = NULL;
n = 0;
while (n < shdr.sh_size &&
(data = elf_getdata(scn, data)) != NULL) {
len = data->d_size / shdr.sh_entsize;
for (i = 0; i < len; i++) {
if (gelf_getsym(data, i, &sym) != &sym) {
bsdar_warnc(bsdar, 0,
"gelf_getsym failed: %s",
elf_errmsg(-1));
continue;
}
/* Keep only global and weak symbols. */
if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL &&
GELF_ST_BIND(sym.st_info) != STB_WEAK)
continue;
/* Keep only defined symbols. */
if (sym.st_shndx == SHN_UNDEF)
continue;
if ((name = elf_strptr(e, tabndx,
sym.st_name)) == NULL) {
bsdar_warnc(bsdar, 0,
"elf_strptr failed: %s",
elf_errmsg(-1));
continue;
}
add_to_ar_sym_table(bsdar, name);
}
}
}
elferr = elf_errno();
if (elferr != 0)
bsdar_warnc(bsdar, 0, "elf_nextscn failed: %s",
elf_errmsg(elferr));
}
/*
* Append to the archive string table buffer.
*/
static void
add_to_ar_str_table(struct bsdar *bsdar, const char *name)
{
if (bsdar->as == NULL) {
bsdar->as_cap = _INIT_AS_CAP;
bsdar->as_sz = 0;
if ((bsdar->as = malloc(bsdar->as_cap)) == NULL)
bsdar_errc(bsdar, errno, "malloc failed");
}
/*
* The space required for holding one member name in the 'as'
* table includes: strlen(name) + (1 for '/') + (1 for '\n') +
* (possibly 1 for padding).
*/
while (bsdar->as_sz + strlen(name) + 3 > bsdar->as_cap) {
bsdar->as_cap *= 2;
bsdar->as = realloc(bsdar->as, bsdar->as_cap);
if (bsdar->as == NULL)
bsdar_errc(bsdar, errno, "realloc failed");
}
strncpy(&bsdar->as[bsdar->as_sz], name, strlen(name));
bsdar->as_sz += strlen(name);
bsdar->as[bsdar->as_sz++] = '/';
bsdar->as[bsdar->as_sz++] = '\n';
}
/*
* Append to the archive symbol table buffer.
*/
static void
add_to_ar_sym_table(struct bsdar *bsdar, const char *name)
{
if (bsdar->s_so == NULL) {
if ((bsdar->s_so = malloc(_INIT_SYMOFF_CAP)) ==
NULL)
bsdar_errc(bsdar, errno, "malloc failed");
bsdar->s_so_cap = _INIT_SYMOFF_CAP;
bsdar->s_cnt = 0;
}
if (bsdar->s_sn == NULL) {
if ((bsdar->s_sn = malloc(_INIT_SYMNAME_CAP)) == NULL)
bsdar_errc(bsdar, errno, "malloc failed");
bsdar->s_sn_cap = _INIT_SYMNAME_CAP;
bsdar->s_sn_sz = 0;
}
if (bsdar->s_cnt * sizeof(uint32_t) >= bsdar->s_so_cap) {
bsdar->s_so_cap *= 2;
bsdar->s_so = realloc(bsdar->s_so, bsdar->s_so_cap);
if (bsdar->s_so == NULL)
bsdar_errc(bsdar, errno, "realloc failed");
}
bsdar->s_so[bsdar->s_cnt] = bsdar->rela_off;
bsdar->s_cnt++;
/*
* The space required for holding one symbol name in the 'sn'
* table includes: strlen(name) + (1 for '\n') + (possibly 1
* for padding).
*/
while (bsdar->s_sn_sz + strlen(name) + 2 > bsdar->s_sn_cap) {
bsdar->s_sn_cap *= 2;
bsdar->s_sn = realloc(bsdar->s_sn, bsdar->s_sn_cap);
if (bsdar->s_sn == NULL)
bsdar_errc(bsdar, errno, "realloc failed");
}
strncpy(&bsdar->s_sn[bsdar->s_sn_sz], name, strlen(name));
bsdar->s_sn_sz += strlen(name);
bsdar->s_sn[bsdar->s_sn_sz++] = '\0';
}

14
as/Makefile Normal file
View File

@ -0,0 +1,14 @@
# $Id: Makefile 2797 2012-12-21 18:10:25Z jkoshy $
TOP= ..
PROG= as
SRCS= as.c
LSRC=
YSRC=
LDADD= -lelftc
MAN= as.1
.include "${TOP}/mk/elftoolchain.prog.mk"

211
as/as.1 Normal file
View File

@ -0,0 +1,211 @@
.\" Copyright (c) 2012 Joseph Koshy <jkoshy@users.sourceforge.net>
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer
.\" in this position and unchanged.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\" $Id: as.1 2798 2012-12-22 09:02:50Z jkoshy $
.\"
.Dd December 22, 2012
.Os
.Dt AS 1
.Sh NAME
.Nm as
.Nd an assembler
.Sh SYNOPSIS
.Nm
.Op Fl D
.Op Fl I Ar dir
.Op Fl J
.Op Fl K
.Op Fl L | Fl -keep-locals
.Op Fl R
.Op Fl V
.Op Fl W | Fl -no-warn
.Op Fl Z
.Op Fl a Ns Oo Ar cdghlns Oc Ns Oo Ar =filename Oc
.Op Fl g | Fl -gen-debug
.Op Fl h | Fl -help
.Op Fl march= Ns Ar cpu Ns Oo ,+ Ns Oo Ar extensions Oc Ns "..." Oc
.Op Fl mtune= Ns Ar cpu
.Op Fl n
.Op Fl o Ar obj
.Op Fl q
.Op Fl -MD Ar file
.Op Fl -defsym Ar symbol Ns = Ns Ar value
.Op Fl -fatal-warnings
.Op Fl -listing-lhs-width
.Op Fl -listing-lhs-width2
.Op Fl -listing-rhs-width
.Op Fl -listing-cont-lines
.Op Fl -statistics
.Op Fl -strip-local-absolute
.Op Fl -target-help
.Op Fl -version
.Op Fl -warn
.Op Ar target-options
.Op Ar
.Sh DESCRIPTION
The
.Nm
utility creates an ELF relocatable object from assembly language
sources.
.Pp
If multiple source files are specified,
.Nm
will read each of them in the order specified.
If no files are specified,
.Nm
will read from standard input.
.Pp
The
.Nm
utility understands the following options:
.Bl -tag -width indent
.It Fl D
Display internal debugging messages.
.It Fl I Ar dir
Add the directory named by argument
.Ar dir
to the search path used by the
.Ic ".include"
directive.
.It Fl J
Suppress warnings about overflows in signed arithmetic.
.It Fl K
Warn about alterations to difference tables if any.
.It Fl L | Fl -keep-locals
Keep local symbols in the output file.
The default is to discard local symbols.
.It Fl R
Merge the
.Li .data
and
.Li .text
sections when creating an object.
.It Fl V
Print the assembler version on standard output before assembling
the inputs.
.It Fl W | Fl -no-warn
Suppress assembler warnings.
.It Fl Z
Generate an output object even if there were errors in the input.
.It Fl a Ns Oo Ar cdghlns Oc Ns Oo Ar =filename Oc
Control generated listings.
The supported flags are:
.Bl -tag -width indent -compact
.It c
Omit listing code in false conditional paths.
.It d
Omit debugging directives.
.It g
Include the version of the assembler and other general information
in the generated listing.
.It h
Include high-level source in the listing.
.It l
Include assembly source in the listing.
.It m
Include macro expansions in the listing.
.It n
Suppress generation of the header and footer in the listing.
.It s
Include symbol information in the listing.
.It = Ns Ar filename
Set the name of the listing file to that specified by argument
.Ar filename .
If specified, this flag must be the last in the list.
.El
If option
.Fl a
is specified without additional arguments, a flag set of
.Sq Ar hls
is assumed.
.It Fl g | Fl -gen-debug
Generate debug information in DWARF format.
.It Fl h | Fl -help
Show a help message and exit.
.It Fl march Ns = Ns Ar cpu Ns Oo ,+ Ns Oo Ar extensions Oc Ns "..." Oc
Generated code for the CPU named by argument
.Ar cpu
with additional instruction set extensions named by the argument
.Ar extensions .
.It Fl mtune Ns = Ns Ar cpu
Optimize the assembled object for the CPU named by the argument
.Ar cpu .
.It Fl n
Do not optimize code alignment.
.It Fl o Ar filename
Write the assembled output to the file named by argument
.Ar filename .
.It Fl q
Suppress warnings.
.It Fl -MD Ar filename
Write dependency information in a form usable by
.Xr make 1
to the file name by argument
.Ar filename .
.It Fl -defsym Ar symbol Ns = Ns Ar value
Define symbol named by the argument
.Ar symbol
as having the value named by
.Ar value .
The argument value is an integer in one of the forms accepted
by
.Xr atoi 3 .
.It Fl -fatal-warnings
Treat all warnings as fatal.
.It Fl -listing-lhs-width Ns = Ns Ar nwords
Set the width of the output data column in a listing to the number
of machine words specified in argument
.Ar nwords .
.It Fl -listing-lhs-width2 Ns = Ns Ar nwords
Set the width of continuation lines for the data column to the number
of machine words specified in argument
.Ar nwords .
.It Fl -listing-rhs-width Ns = Ns Ar nchars
Set the maximum displayed width of an input source line to the number
of characters specified by argument
.Ar nchars .
.It Fl -listing-cont-lines Ns = Ns Ar nlines
Set the maximum number of listing lines generated by one input source
line to
.Ar nlines Ns No + Ns 1 .
.It Fl -statistics
Print statistics for the run at exit.
.It Fl -strip-local-absolute
Remove local absolute symbols from the generated output.
.It Fl -target-help
Display help for the target CPU.
.It Fl -version
Print a version identifier and exit.
.It Fl -warn
Print warnings.
.El
.Sh EXIT STATUS
.Ex -std
.Sh SEE ALSO
.Xr elfcopy 1 ,
.Xr ld 1 ,
.Xr nm 1 ,
.Xr strings 1 ,
.Xr strip 1 ,
.Xr elf 5

216
as/as.c Normal file
View File

@ -0,0 +1,216 @@
/*-
* Copyright (c) 2012 Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <getopt.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "_elftc.h"
#include <libelftc.h>
ELFTC_VCSID("$Id: as.c 2799 2012-12-22 09:03:29Z jkoshy $");
enum as_long_option_index {
AS_OPT_DEFSYM,
AS_OPT_FATAL_WARNINGS,
AS_OPT_LCL,
AS_OPT_LLW,
AS_OPT_LLW2,
AS_OPT_LRW,
AS_OPT_MD,
AS_OPT_STATISTICS,
AS_OPT_STRIP_LOCAL_ABSOLUTE,
AS_OPT_TARGET_HELP,
AS_OPT_VERSION,
AS_OPT_WARN,
AS_OPT__LAST
};
struct as_options {
unsigned int as_listing_flags;
};
#define AS_OPTION_SHORT_OPTIONS ":a:fghm:no:qswDI:JKLMRVWXZ"
const struct option as_option_long_options[] = {
{ "defsym", required_argument, NULL, AS_OPT_DEFSYM },
{ "fatal-warnings", no_argument, NULL, AS_OPT_FATAL_WARNINGS },
{ "gen-debug", no_argument, NULL, 'g' },
{ "help", no_argument, NULL, 'h' },
{ "keep-locals", no_argument, NULL, 'L' },
{ "listing-lhs-width", required_argument, NULL, AS_OPT_LLW },
{ "listing-lhs-width2", required_argument, NULL, AS_OPT_LLW2 },
{ "listing-rhs-width", required_argument, NULL, AS_OPT_LRW },
{ "listing-cont-lines", required_argument, NULL, AS_OPT_LCL },
{ "mri", no_argument, NULL, 'M' },
{ "no-warn", no_argument, NULL, 'W' },
{ "statistics", no_argument, NULL, AS_OPT_STATISTICS },
{ "strip-local-absolute", no_argument, NULL,
AS_OPT_STRIP_LOCAL_ABSOLUTE },
{ "target-help", no_argument, NULL, AS_OPT_TARGET_HELP },
{ "version", no_argument, NULL, AS_OPT_VERSION },
{ "warn", no_argument, NULL, AS_OPT_WARN },
{ "MD", required_argument, NULL, AS_OPT_MD },
{ NULL, 0, NULL, 0 }
};
#define AS_OPTION_LISTING_DEFAULT "hls"
#define AS_OPTION_USAGE_MESSAGE "\
Usage: %s [options] file...\n\
Assemble an ELF object.\n\n\
Options:\n\
-D Print assembler debug messages.\n\
-I DIR Add directory to the search list.\n\
-J Suppress warnings about signed overflows.\n\
-K Warn about alterations to difference tables.\n\
-L | --keep-locals Keep local symbols.\n\
-R Merge the data and text sections.\n\
-V Display the assembler version number.\n\
-W | --no-warn Suppress warnings.\n\
-Z Generate the object even if there are errors.\n\
-a[listing-options...] Control assembler listings.\n\
-g | --gen-debug Generate debugging information.\n\
-h | --help Show a help message.\n\
-march=CPU[,+EXT...] Generate code for cpu CPU and extensions EXT.\n\
-mtune=CPU Optimize for cpu CPU.\n\
-n Do not optimize code alignment.\n\
-o OBJ Write the assembled object to file OBJ.\n\
-q Suppress some warnings.\n\
--MD FILE Write dependency information to FILE.\n\
--defsym SYMBOL=VALUE Define symbol SYMBOL with value VALUE.\n\
--fatal-warnings Treat warnings as fatal errors.\n\
--listing-lhs-width=NUM Set width of the output data column.\n\
--listing-lhs-width2=NUM Set the width of continuation lines.\n\
--listing-rhs-width=NUM Set the max width of source lines.\n\
--listing-cont-lines=NUM Set the maximum number of continuation lines.\n\
--statistics Print statistics at exit.\n\
--strip-local-absolute Strip local absolute symbols.\n\
--target-help Show target-specific help messages.\n\
--version Print a version identifier and exit.\n\
--warn Print warnings.\n\
[target options] Target specific options.\n\n\
Options '-f', '-s', '-w', '-M', '-X' and '--mri' are accepted for\n\
compatibility with other assemblers, but are ignored.\n"
void
as_option_usage(int iserror, const char *format, ...)
{
va_list args;
if (format) {
va_start(args, format);
vwarnx(format, args);
va_end(args);
}
(void) fprintf(iserror ? stderr : stdout,
AS_OPTION_USAGE_MESSAGE, ELFTC_GETPROGNAME());
exit(iserror != 0);
}
static void
as_option_listing(char *flags)
{
(void) flags;
}
int
main(int argc, char **argv)
{
int option, option_index;
opterr = 0; /* Suppress error messages from getopt(). */
for (option_index = -1;
(option = getopt_long(argc, argv, AS_OPTION_SHORT_OPTIONS,
as_option_long_options, &option_index)) >= 0;
option_index = -1)
{
switch (option) {
case AS_OPT_VERSION:
/*
* Print a version identifier and exit.
*/
(void) printf("%s (%s)\n",
ELFTC_GETPROGNAME(), elftc_version());
exit(0);
break;
case 'h': /* Display a help message. */
as_option_usage(0, NULL);
break;
case 'f': case 's': case 'w': case 'M': case 'X':
/*
* These options are accepted for compatibility
* reasons, but are ignored.
*/
break;
case ':':
/*
* A missing option argument: if the user
* supplied a bare '-a', supply a default set
* of listing control flags.
*/
if (optopt == 'a')
as_option_listing(AS_OPTION_LISTING_DEFAULT);
else
errx(1, "option \"-%c\" expects an "
"argument.", optopt);
break;
case '?': /* An unknown option. */
if (optopt)
as_option_usage(1,
"ERROR: unrecognized option '-%c'.",
optopt);
else
as_option_usage(1,
"ERROR: Unrecognized option \"--%s\".",
argv[optind-1]);
break;
default:
if (option_index >= 0)
errx(1,
"ERROR: option \"--%s\" is unimplemented.",
as_option_long_options[option_index]);
else
errx(1,
"ERROR: option '-%c' is unimplemented.",
option);
}
}
exit(0);
}

9
brandelf/Makefile Normal file
View File

@ -0,0 +1,9 @@
# $Id: Makefile 2066 2011-10-26 15:40:28Z jkoshy $
TOP= ..
PROG= brandelf
WARNS?= 6
LDADD= -lelftc -lelf
.include "${TOP}/mk/elftoolchain.prog.mk"

149
brandelf/brandelf.1 Normal file
View File

@ -0,0 +1,149 @@
.\" Copyright (c) 1997
.\" John-Mark Gurney. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. Neither the name of the author nor the names of any co-contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY John-Mark Gurney AND CONTRIBUTORS ``AS IS''
.\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD: src/usr.bin/brandelf/brandelf.1,v 1.17 2007/03/09 14:36:18 ru Exp $
.\" $Id: brandelf.1 2245 2011-11-29 08:11:00Z jkoshy $
.\"
.Dd November 29, 2011
.Dt BRANDELF 1
.Os
.Sh NAME
.Nm brandelf
.Nd mark an ELF binary for a specific ABI
.Sh SYNOPSIS
.Nm
.Op Fl V | Fl -version
.Op Fl f Ar ELF_ABI_number
.Op Fl h | Fl -help
.Op Fl l
.Op Fl t Ar brand
.Op Fl v
.Ar
.Sh DESCRIPTION
The
.Nm
utility marks an ELF binary to be run under a certain ABI.
.Pp
The options are as follows:
.Bl -tag -width indent
.It Fl f Ar ELF_ABI_number
Forces branding with the supplied ELF ABI number.
Incompatible with the
.Fl t
option.
These values are assigned by SCO/USL.
.It Fl h | Fl -help
Print a usage message and exit.
.It Fl l
Writes the list of all known ELF types to standard output.
.It Fl t Ar brand
Brands the given ELF binaries to be of the ABI specified by argument
.Ar brand .
Supported ABIs include
.Dq Li 86Open ,
.Dq Li AIX ,
.Dq Li ARM ,
.Dq Li AROS ,
.Dq Li FreeBSD ,
.Dq Li GNU ,
.Dq Li HP/UX ,
.Dq Li Hurd ,
.Dq Li IRIX ,
.Dq Li Linux
(an alias for
.Dq Li GNU ) ,
.Dq Li Modesto ,
.Dq Li NSK ,
.Dq Li NetBSD ,
.Dq Li None ,
.Dq Li OpenBSD ,
.Dq Li OpenVMS ,
.Dq Li Standalone ,
.Dq Li SVR4
(an alias for
.Dq Li None ) ,
.Dq Li Solaris
and
.Dq Li Tru64 .
.It Fl v
Turns on verbose output.
.It Fl V | Fl -version
Print a version identifier and exit.
.El
.Pp
If the options
.Fl f Ar ELF_ABI_number
or
.Fl t Ar brand
were specified,
.Nm
will brand the files named by command-line arguments
.Ar
to be of type
.Ar ELF_ABI_number
or
.Ar brand
respectively.
.Pp
If neither of the
.Fl f
or
.Fl t
options were specified,
.Nm
will display the current branding for the files named by the arguments
.Ar .
.Sh EXIT STATUS
Exit status is 0 on success, and 1 if the command
fails if a file does not exist, is too short, fails to brand properly,
or the brand requested is not one of the known types and the
.Fl f
option is not set.
.Sh EXAMPLES
The following is an example of a typical usage
of the
.Nm
command:
.Bd -literal -offset indent
brandelf file
brandelf -t GNU file
.Ed
.Sh SEE ALSO
.Rs
.%A The Santa Cruz Operation, Inc.
.%T System V Application Binary Interface
.%D April 29, 1998 (DRAFT)
.%O http://www.sco.com/developer/devspecs/
.Re
.Sh HISTORY
The
.Nm
manual page first appeared in
.Fx 2.2 .
.Sh AUTHORS
This manual page was written by
.An John-Mark Gurney Aq gurney_j@efn.org .

314
brandelf/brandelf.c Normal file
View File

@ -0,0 +1,314 @@
/*-
* Copyright (c) 2008 Hyogeol Lee
* Copyright (c) 2000, 2001 David O'Brien
* Copyright (c) 1996 Søren Schmidt
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <gelf.h>
#include <getopt.h>
#include <libelf.h>
#include <libelftc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "_elftc.h"
ELFTC_VCSID("$Id: brandelf.c 2324 2011-12-12 06:36:27Z jkoshy $");
static int elftype(const char *);
static const char *iselftype(int);
static void printelftypes(void);
static void printversion(void);
static void usage(void);
struct ELFtypes {
const char *str;
int value;
};
/* XXX - any more types? */
static struct ELFtypes elftypes[] = {
{ "86Open", ELFOSABI_86OPEN },
{ "AIX", ELFOSABI_AIX },
{ "ARM", ELFOSABI_ARM },
{ "AROS", ELFOSABI_AROS },
{ "FreeBSD", ELFOSABI_FREEBSD },
{ "GNU", ELFOSABI_GNU },
{ "HP/UX", ELFOSABI_HPUX},
{ "Hurd", ELFOSABI_HURD },
{ "IRIX", ELFOSABI_IRIX },
{ "Linux", ELFOSABI_GNU },
{ "Modesto", ELFOSABI_MODESTO },
{ "NSK", ELFOSABI_NSK },
{ "NetBSD", ELFOSABI_NETBSD},
{ "None", ELFOSABI_NONE},
{ "OpenBSD", ELFOSABI_OPENBSD },
{ "OpenVMS", ELFOSABI_OPENVMS },
{ "Standalone", ELFOSABI_STANDALONE },
{ "SVR4", ELFOSABI_NONE },
{ "Solaris", ELFOSABI_SOLARIS },
{ "Tru64", ELFOSABI_TRU64 }
};
static struct option brandelf_longopts[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0 }
};
int
main(int argc, char **argv)
{
GElf_Ehdr ehdr;
Elf *elf;
Elf_Kind kind;
int type = ELFOSABI_NONE;
int retval = 0;
int ch, change = 0, verbose = 0, force = 0, listed = 0;
if (elf_version(EV_CURRENT) == EV_NONE)
errx(EXIT_FAILURE, "elf_version error");
while ((ch = getopt_long(argc, argv, "Vf:hlt:v", brandelf_longopts,
NULL)) != -1)
switch (ch) {
case 'f':
if (change)
errx(EXIT_FAILURE, "ERROR: the -f option is "
"incompatible with the -t option.");
force = 1;
type = atoi(optarg);
if (errno == ERANGE || type < 0 || type > 255) {
warnx("ERROR: invalid argument to option "
"-f: %s", optarg);
usage();
}
break;
case 'h':
usage();
break;
case 'l':
printelftypes();
listed = 1;
break;
case 'v':
verbose = 1;
break;
case 't':
if (force)
errx(EXIT_FAILURE, "the -t option is "
"incompatible with the -f option.");
if ((type = elftype(optarg)) == -1) {
warnx("ERROR: invalid ELF type '%s'", optarg);
usage();
}
change = 1;
break;
case 'V':
printversion();
break;
default:
usage();
}
argc -= optind;
argv += optind;
if (!argc) {
if (listed)
exit(0);
else {
warnx("no file(s) specified");
usage();
}
}
while (argc) {
int fd;
elf = NULL;
if ((fd = open(argv[0], (change || force) ? O_RDWR :
O_RDONLY, 0)) < 0) {
warn("error opening file %s", argv[0]);
retval = 1;
goto fail;
}
if ((elf = elf_begin(fd, (change || force) ? ELF_C_RDWR :
ELF_C_READ, NULL)) == NULL) {
warnx("elf_begin failed: %s", elf_errmsg(-1));
retval = 1;
goto fail;
}
if ((kind = elf_kind(elf)) != ELF_K_ELF) {
if (kind == ELF_K_AR)
warnx("file '%s' is an archive.", argv[0]);
else
warnx("file '%s' is not an ELF file.",
argv[0]);
retval = 1;
goto fail;
}
if (gelf_getehdr(elf, &ehdr) == NULL) {
warnx("gelf_getehdr: %s", elf_errmsg(-1));
retval = 1;
goto fail;
}
if (!change && !force) {
fprintf(stdout,
"File '%s' is of brand '%s' (%u).\n",
argv[0], iselftype(ehdr.e_ident[EI_OSABI]),
ehdr.e_ident[EI_OSABI]);
if (!iselftype(type)) {
warnx("ELF ABI Brand '%u' is unknown",
type);
printelftypes();
}
} else {
/*
* Keep the existing layout of the ELF object.
*/
if (elf_flagelf(elf, ELF_C_SET, ELF_F_LAYOUT) == 0) {
warnx("elf_flagelf failed: %s",
elf_errmsg(-1));
retval = 1;
goto fail;
}
/*
* Update the ABI type.
*/
ehdr.e_ident[EI_OSABI] = type;
if (gelf_update_ehdr(elf, &ehdr) == 0) {
warnx("gelf_update_ehdr error: %s",
elf_errmsg(-1));
retval = 1;
goto fail;
}
/*
* Write back changes.
*/
if (elf_update(elf, ELF_C_WRITE) == -1) {
warnx("elf_update error: %s", elf_errmsg(-1));
retval = 1;
goto fail;
}
}
fail:
if (elf)
elf_end(elf);
if (fd >= 0 && close(fd) == -1) {
warnx("%s: close error", argv[0]);
retval = 1;
}
argc--;
argv++;
}
return (retval);
}
#define USAGE_MESSAGE "\
Usage: %s [options] file...\n\
Set or display the ABI field for an ELF object.\n\n\
Supported options are:\n\
-f NUM Set the ELF ABI to the number 'NUM'.\n\
-h | --help Print a usage message and exit.\n\
-l List known ELF ABI names.\n\
-t ABI Set the ELF ABI to the value named by \"ABI\".\n\
-v Be verbose.\n\
-V | --version Print a version identifier and exit.\n"
static void
usage(void)
{
(void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
exit(1);
}
static void
printversion(void)
{
(void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version());
exit(0);
}
static const char *
iselftype(int etype)
{
size_t elfwalk;
for (elfwalk = 0;
elfwalk < sizeof(elftypes)/sizeof(elftypes[0]);
elfwalk++)
if (etype == elftypes[elfwalk].value)
return (elftypes[elfwalk].str);
return (0);
}
static int
elftype(const char *elfstrtype)
{
size_t elfwalk;
for (elfwalk = 0;
elfwalk < sizeof(elftypes)/sizeof(elftypes[0]);
elfwalk++)
if (strcasecmp(elfstrtype, elftypes[elfwalk].str) == 0)
return (elftypes[elfwalk].value);
return (-1);
}
static void
printelftypes(void)
{
size_t elfwalk;
(void) printf("Known ELF types are: ");
for (elfwalk = 0;
elfwalk < sizeof(elftypes)/sizeof(elftypes[0]);
elfwalk++)
(void) printf("%s(%u) ", elftypes[elfwalk].str,
elftypes[elfwalk].value);
(void) printf("\n");
}

15
common/Makefile Normal file
View File

@ -0,0 +1,15 @@
# $Id: Makefile 2606 2012-10-02 17:52:57Z jkoshy $
TOP= ..
INCS= elfdefinitions.h
INCSDIR= /usr/include
.PHONY: all clean clobber depend obj
all depend obj:
clean clobber:
rm -f ${CLEANFILES}
.include "${TOP}/mk/elftoolchain.inc.mk"

458
common/_elftc.h Normal file
View File

@ -0,0 +1,458 @@
/*-
* Copyright (c) 2009 Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: _elftc.h 2922 2013-03-17 22:53:15Z kaiwang27 $
*/
/**
** Miscellanous definitions needed by multiple components.
**/
#ifndef _ELFTC_H
#define _ELFTC_H
#ifndef NULL
#define NULL ((void *) 0)
#endif
#ifndef offsetof
#define offsetof(T, M) ((int) &((T*) 0) -> M)
#endif
/* --QUEUE-MACROS-- [[ */
/*
* Supply macros missing from <sys/queue.h>
*/
/*
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef SLIST_FOREACH_SAFE
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = SLIST_FIRST((head)); \
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
(var) = (tvar))
#endif
#ifndef STAILQ_CONCAT
#define STAILQ_CONCAT(head1, head2) do { \
if (!STAILQ_EMPTY((head2))) { \
*(head1)->stqh_last = (head2)->stqh_first; \
(head1)->stqh_last = (head2)->stqh_last; \
STAILQ_INIT((head2)); \
} \
} while (/*CONSTCOND*/0)
#endif
#ifndef STAILQ_EMPTY
#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
#endif
#ifndef STAILQ_ENTRY
#define STAILQ_ENTRY(type) \
struct { \
struct type *stqe_next; /* next element */ \
}
#endif
#ifndef STAILQ_FIRST
#define STAILQ_FIRST(head) ((head)->stqh_first)
#endif
#ifndef STAILQ_HEAD
#define STAILQ_HEAD(name, type) \
struct name { \
struct type *stqh_first; /* first element */ \
struct type **stqh_last; /* addr of last next element */ \
}
#endif
#ifndef STAILQ_HEAD_INITIALIZER
#define STAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).stqh_first }
#endif
#ifndef STAILQ_FOREACH
#define STAILQ_FOREACH(var, head, field) \
for ((var) = ((head)->stqh_first); \
(var); \
(var) = ((var)->field.stqe_next))
#endif
#ifndef STAILQ_FOREACH_SAFE
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = STAILQ_FIRST((head)); \
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#endif
#ifndef STAILQ_INIT
#define STAILQ_INIT(head) do { \
(head)->stqh_first = NULL; \
(head)->stqh_last = &(head)->stqh_first; \
} while (/*CONSTCOND*/0)
#endif
#ifndef STAILQ_INSERT_HEAD
#define STAILQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \
(head)->stqh_last = &(elm)->field.stqe_next; \
(head)->stqh_first = (elm); \
} while (/*CONSTCOND*/0)
#endif
#ifndef STAILQ_INSERT_TAIL
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.stqe_next = NULL; \
*(head)->stqh_last = (elm); \
(head)->stqh_last = &(elm)->field.stqe_next; \
} while (/*CONSTCOND*/0)
#endif
#ifndef STAILQ_INSERT_AFTER
#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\
(head)->stqh_last = &(elm)->field.stqe_next; \
(listelm)->field.stqe_next = (elm); \
} while (/*CONSTCOND*/0)
#endif
#ifndef STAILQ_LAST
#define STAILQ_LAST(head, type, field) \
(STAILQ_EMPTY((head)) ? \
NULL : ((struct type *)(void *) \
((char *)((head)->stqh_last) - offsetof(struct type, field))))
#endif
#ifndef STAILQ_NEXT
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
#endif
#ifndef STAILQ_REMOVE
#define STAILQ_REMOVE(head, elm, type, field) do { \
if ((head)->stqh_first == (elm)) { \
STAILQ_REMOVE_HEAD((head), field); \
} else { \
struct type *curelm = (head)->stqh_first; \
while (curelm->field.stqe_next != (elm)) \
curelm = curelm->field.stqe_next; \
if ((curelm->field.stqe_next = \
curelm->field.stqe_next->field.stqe_next) == NULL) \
(head)->stqh_last = &(curelm)->field.stqe_next; \
} \
} while (/*CONSTCOND*/0)
#endif
#ifndef STAILQ_REMOVE_HEAD
#define STAILQ_REMOVE_HEAD(head, field) do { \
if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == \
NULL) \
(head)->stqh_last = &(head)->stqh_first; \
} while (/*CONSTCOND*/0)
#endif
/*
* The STAILQ_SORT macro is adapted from Simon Tatham's O(n*log(n))
* mergesort algorithm.
*/
#ifndef STAILQ_SORT
#define STAILQ_SORT(head, type, field, cmp) do { \
STAILQ_HEAD(, type) _la, _lb; \
struct type *_p, *_q, *_e; \
int _i, _sz, _nmerges, _psz, _qsz; \
\
_sz = 1; \
do { \
_nmerges = 0; \
STAILQ_INIT(&_lb); \
while (!STAILQ_EMPTY((head))) { \
_nmerges++; \
STAILQ_INIT(&_la); \
_psz = 0; \
for (_i = 0; _i < _sz && !STAILQ_EMPTY((head)); \
_i++) { \
_e = STAILQ_FIRST((head)); \
if (_e == NULL) \
break; \
_psz++; \
STAILQ_REMOVE_HEAD((head), field); \
STAILQ_INSERT_TAIL(&_la, _e, field); \
} \
_p = STAILQ_FIRST(&_la); \
_qsz = _sz; \
_q = STAILQ_FIRST((head)); \
while (_psz > 0 || (_qsz > 0 && _q != NULL)) { \
if (_psz == 0) { \
_e = _q; \
_q = STAILQ_NEXT(_q, field); \
STAILQ_REMOVE_HEAD((head), \
field); \
_qsz--; \
} else if (_qsz == 0 || _q == NULL) { \
_e = _p; \
_p = STAILQ_NEXT(_p, field); \
STAILQ_REMOVE_HEAD(&_la, field);\
_psz--; \
} else if (cmp(_p, _q) <= 0) { \
_e = _p; \
_p = STAILQ_NEXT(_p, field); \
STAILQ_REMOVE_HEAD(&_la, field);\
_psz--; \
} else { \
_e = _q; \
_q = STAILQ_NEXT(_q, field); \
STAILQ_REMOVE_HEAD((head), \
field); \
_qsz--; \
} \
STAILQ_INSERT_TAIL(&_lb, _e, field); \
} \
} \
(head)->stqh_first = _lb.stqh_first; \
(head)->stqh_last = _lb.stqh_last; \
_sz *= 2; \
} while (_nmerges > 1); \
} while (/*CONSTCOND*/0)
#endif
#ifndef TAILQ_FOREACH_SAFE
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = TAILQ_FIRST((head)); \
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#endif
/* ]] --QUEUE-MACROS-- */
/*
* VCS Ids.
*/
#ifndef ELFTC_VCSID
#if defined(__DragonFly__)
#define ELFTC_VCSID(ID) __RCSID(ID)
#endif
#if defined(__FreeBSD__)
#define ELFTC_VCSID(ID) __FBSDID(ID)
#endif
#if defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)
#if defined(__GNUC__)
#define ELFTC_VCSID(ID) __asm__(".ident\t\"" ID "\"")
#else
#define ELFTC_VCSID(ID) /**/
#endif
#endif
#if defined(__minix)
#if defined(__GNUC__)
#define ELFTC_VCSID(ID) __asm__(".ident\t\"" ID "\"")
#else
#define ELFTC_VCSID(ID) /**/
#endif /* __GNU__ */
#endif
#if defined(__NetBSD__)
#define ELFTC_VCSID(ID) __RCSID(ID)
#endif
#if defined(__OpenBSD__)
#if defined(__GNUC__)
#define ELFTC_VCSID(ID) __asm__(".ident\t\"" ID "\"")
#else
#define ELFTC_VCSID(ID) /**/
#endif /* __GNUC__ */
#endif
#endif /* ELFTC_VCSID */
/*
* Provide an equivalent for getprogname(3).
*/
#ifndef ELFTC_GETPROGNAME
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__minix) || \
defined(__NetBSD__)
#include <stdlib.h>
#define ELFTC_GETPROGNAME() getprogname()
#endif /* __DragonFly__ || __FreeBSD__ || __minix || __NetBSD__ */
#if defined(__GLIBC__)
/*
* GLIBC based systems have a global 'char *' pointer referencing
* the executable's name.
*/
extern const char *program_invocation_short_name;
#define ELFTC_GETPROGNAME() program_invocation_short_name
#endif /* __GLIBC__ */
#if defined(__OpenBSD__)
extern const char *__progname;
#define ELFTC_GETPROGNAME() __progname
#endif /* __OpenBSD__ */
#endif /* ELFTC_GETPROGNAME */
/**
** Per-OS configuration.
**/
#if defined(__DragonFly__)
#include <osreldate.h>
#include <sys/endian.h>
#define ELFTC_BYTE_ORDER _BYTE_ORDER
#define ELFTC_BYTE_ORDER_LITTLE_ENDIAN _LITTLE_ENDIAN
#define ELFTC_BYTE_ORDER_BIG_ENDIAN _BIG_ENDIAN
#define ELFTC_HAVE_MMAP 1
#endif
#if defined(__GLIBC__)
#include <endian.h>
#define ELFTC_BYTE_ORDER __BYTE_ORDER
#define ELFTC_BYTE_ORDER_LITTLE_ENDIAN __LITTLE_ENDIAN
#define ELFTC_BYTE_ORDER_BIG_ENDIAN __BIG_ENDIAN
#define ELFTC_HAVE_MMAP 1
/*
* Debian GNU/Linux and Debian GNU/kFreeBSD do not have strmode(3).
*/
#define ELFTC_HAVE_STRMODE 0
/* Whether we need to supply {be,le}32dec. */
#define ELFTC_NEED_BYTEORDER_EXTENSIONS 1
#define roundup2 roundup
#endif /* __GLIBC__ */
#if defined(__FreeBSD__)
#include <osreldate.h>
#include <sys/endian.h>
#define ELFTC_BYTE_ORDER _BYTE_ORDER
#define ELFTC_BYTE_ORDER_LITTLE_ENDIAN _LITTLE_ENDIAN
#define ELFTC_BYTE_ORDER_BIG_ENDIAN _BIG_ENDIAN
#define ELFTC_HAVE_MMAP 1
#define ELFTC_HAVE_STRMODE 1
#if __FreeBSD_version <= 900000
#define ELFTC_BROKEN_YY_NO_INPUT 1
#endif
#endif /* __FreeBSD__ */
#if defined(__minix)
#define ELFTC_HAVE_MMAP 0
#endif /* __minix */
#if defined(__NetBSD__)
#include <sys/param.h>
#include <sys/endian.h>
#define ELFTC_BYTE_ORDER _BYTE_ORDER
#define ELFTC_BYTE_ORDER_LITTLE_ENDIAN _LITTLE_ENDIAN
#define ELFTC_BYTE_ORDER_BIG_ENDIAN _BIG_ENDIAN
#define ELFTC_HAVE_MMAP 1
#define ELFTC_HAVE_STRMODE 1
#if __NetBSD_Version__ <= 599002100
/* from src/doc/CHANGES: flex(1): Import flex-2.5.35 [christos 20091025] */
/* and 5.99.21 was from Wed Oct 21 21:28:36 2009 UTC */
# define ELFTC_BROKEN_YY_NO_INPUT 1
#endif
#endif /* __NetBSD __ */
#if defined(__OpenBSD__)
#include <sys/param.h>
#include <sys/endian.h>
#define ELFTC_BYTE_ORDER _BYTE_ORDER
#define ELFTC_BYTE_ORDER_LITTLE_ENDIAN _LITTLE_ENDIAN
#define ELFTC_BYTE_ORDER_BIG_ENDIAN _BIG_ENDIAN
#define ELFTC_HAVE_MMAP 1
#define ELFTC_HAVE_STRMODE 1
#define ELFTC_NEED_BYTEORDER_EXTENSIONS 1
#define roundup2 roundup
#endif /* __OpenBSD__ */
#endif /* _ELFTC_H */

2614
common/elfdefinitions.h Normal file

File diff suppressed because it is too large Load Diff

47
common/native-elf-format Executable file
View File

@ -0,0 +1,47 @@
#!/bin/sh
#
# $Id: native-elf-format 2064 2011-10-26 15:12:32Z jkoshy $
#
# Find the native ELF format for a host platform by compiling a
# test object and examining the resulting object.
#
# This script is used if there is no easy way to determine this
# information statically at compile time.
program=`basename $0`
tmp_c=`mktemp -u nefXXXXXX`.c
tmp_o=`echo ${tmp_c} | sed -e 's/.c$/.o/'`
trap "rm -f ${tmp_c} ${tmp_o}" 0 1 2 3 15
touch ${tmp_c}
echo "/* Generated by ${program} on `date` */"
cc -c ${tmp_c} -o ${tmp_o}
readelf -h ${tmp_o} | awk '
$1 ~ "Class:" {
sub("ELF","",$2); elfclass = $2;
}
$1 ~ "Data:" {
if (match($0, "little")) {
elfdata = "LSB";
} else {
elfdata = "MSB";
}
}
$1 ~ "Machine:" {
if (match($0, "Intel.*386")) {
elfarch = "EM_386";
} else if (match($0, ".*X86-64")) {
elfarch = "EM_X86_64";
} else {
elfarch = "unknown";
}
}
END {
printf("#define ELFTC_CLASS ELFCLASS%s\n", elfclass);
printf("#define ELFTC_ARCH %s\n", elfarch);
printf("#define ELFTC_BYTEORDER ELFDATA2%s\n", elfdata);
}'

13
common/os.Linux.mk Normal file
View File

@ -0,0 +1,13 @@
#
# Build recipes for Linux based operating systems.
#
# $Id: os.Linux.mk 2064 2011-10-26 15:12:32Z jkoshy $
_NATIVE_ELF_FORMAT = native-elf-format
.BEGIN: ${_NATIVE_ELF_FORMAT}.h
${_NATIVE_ELF_FORMAT}.h:
${.CURDIR}/${_NATIVE_ELF_FORMAT} > ${.TARGET} || rm ${.TARGET}
CLEANFILES += ${_NATIVE_ELF_FORMAT}.h

237
common/utarray.h Normal file
View File

@ -0,0 +1,237 @@
/*
Copyright (c) 2008-2013, Troy D. Hanson http://uthash.sourceforge.net
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* $Id: utarray.h 2694 2012-11-24 17:11:58Z kaiwang27 $ */
/* a dynamic array implementation using macros
* see http://uthash.sourceforge.net/utarray
*/
#ifndef UTARRAY_H
#define UTARRAY_H
#define UTARRAY_VERSION 1.9.7
#ifdef __GNUC__
#define _UNUSED_ __attribute__ ((__unused__))
#else
#define _UNUSED_
#endif
#include <stddef.h> /* size_t */
#include <string.h> /* memset, etc */
#include <stdlib.h> /* exit */
#ifndef oom
#define oom() exit(-1)
#endif
typedef void (ctor_f)(void *dst, const void *src);
typedef void (dtor_f)(void *elt);
typedef void (init_f)(void *elt);
typedef struct {
size_t sz;
init_f *init;
ctor_f *copy;
dtor_f *dtor;
} UT_icd;
typedef struct {
unsigned i,n;/* i: index of next available slot, n: num slots */
UT_icd icd; /* initializer, copy and destructor functions */
char *d; /* n slots of size icd->sz*/
} UT_array;
#define utarray_init(a,_icd) do { \
memset(a,0,sizeof(UT_array)); \
(a)->icd=*_icd; \
} while(0)
#define utarray_done(a) do { \
if ((a)->n) { \
if ((a)->icd.dtor) { \
size_t _ut_i; \
for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \
(a)->icd.dtor(utarray_eltptr(a,_ut_i)); \
} \
} \
free((a)->d); \
} \
(a)->n=0; \
} while(0)
#define utarray_new(a,_icd) do { \
a=(UT_array*)malloc(sizeof(UT_array)); \
utarray_init(a,_icd); \
} while(0)
#define utarray_free(a) do { \
utarray_done(a); \
free(a); \
} while(0)
#define utarray_reserve(a,by) do { \
if (((a)->i+by) > ((a)->n)) { \
while(((a)->i+by) > ((a)->n)) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \
if ( ((a)->d=(char*)realloc((a)->d, (a)->n*(a)->icd.sz)) == NULL) oom(); \
} \
} while(0)
#define utarray_push_back(a,p) do { \
utarray_reserve(a,1); \
if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,(a)->i++), p); } \
else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd.sz); }; \
} while(0)
#define utarray_pop_back(a) do { \
if ((a)->icd.dtor) { (a)->icd.dtor( _utarray_eltptr(a,--((a)->i))); } \
else { (a)->i--; } \
} while(0)
#define utarray_extend_back(a) do { \
utarray_reserve(a,1); \
if ((a)->icd.init) { (a)->icd.init(_utarray_eltptr(a,(a)->i)); } \
else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd.sz); } \
(a)->i++; \
} while(0)
#define utarray_len(a) ((a)->i)
#define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL)
#define _utarray_eltptr(a,j) ((char*)((a)->d + ((a)->icd.sz*(j) )))
#define utarray_insert(a,p,j) do { \
utarray_reserve(a,1); \
if (j > (a)->i) break; \
if ((j) < (a)->i) { \
memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j), \
((a)->i - (j))*((a)->icd.sz)); \
} \
if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,j), p); } \
else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); }; \
(a)->i++; \
} while(0)
#define utarray_inserta(a,w,j) do { \
if (utarray_len(w) == 0) break; \
if (j > (a)->i) break; \
utarray_reserve(a,utarray_len(w)); \
if ((j) < (a)->i) { \
memmove(_utarray_eltptr(a,(j)+utarray_len(w)), \
_utarray_eltptr(a,j), \
((a)->i - (j))*((a)->icd.sz)); \
} \
if ((a)->icd.copy) { \
size_t _ut_i; \
for(_ut_i=0;_ut_i<(w)->i;_ut_i++) { \
(a)->icd.copy(_utarray_eltptr(a,j+_ut_i), _utarray_eltptr(w,_ut_i)); \
} \
} else { \
memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0), \
utarray_len(w)*((a)->icd.sz)); \
} \
(a)->i += utarray_len(w); \
} while(0)
#define utarray_resize(dst,num) do { \
size_t _ut_i; \
if (dst->i > (size_t)(num)) { \
if ((dst)->icd.dtor) { \
for(_ut_i=num; _ut_i < dst->i; _ut_i++) { \
(dst)->icd.dtor(utarray_eltptr(dst,_ut_i)); \
} \
} \
} else if (dst->i < (size_t)(num)) { \
utarray_reserve(dst,num-dst->i); \
if ((dst)->icd.init) { \
for(_ut_i=dst->i; _ut_i < num; _ut_i++) { \
(dst)->icd.init(utarray_eltptr(dst,_ut_i)); \
} \
} else { \
memset(_utarray_eltptr(dst,dst->i),0,(dst)->icd.sz*(num-dst->i)); \
} \
} \
dst->i = num; \
} while(0)
#define utarray_concat(dst,src) do { \
utarray_inserta((dst),(src),utarray_len(dst)); \
} while(0)
#define utarray_erase(a,pos,len) do { \
if ((a)->icd.dtor) { \
size_t _ut_i; \
for(_ut_i=0; _ut_i < len; _ut_i++) { \
(a)->icd.dtor(utarray_eltptr((a),pos+_ut_i)); \
} \
} \
if ((a)->i > (pos+len)) { \
memmove( _utarray_eltptr((a),pos), _utarray_eltptr((a),pos+len), \
(((a)->i)-(pos+len))*((a)->icd.sz)); \
} \
(a)->i -= (len); \
} while(0)
#define utarray_renew(a,u) do { \
if (a) utarray_clear(a); \
else utarray_new((a),(u)); \
} while(0)
#define utarray_clear(a) do { \
if ((a)->i > 0) { \
if ((a)->icd.dtor) { \
size_t _ut_i; \
for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \
(a)->icd.dtor(utarray_eltptr(a,_ut_i)); \
} \
} \
(a)->i = 0; \
} \
} while(0)
#define utarray_sort(a,cmp) do { \
qsort((a)->d, (a)->i, (a)->icd.sz, cmp); \
} while(0)
#define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd.sz,cmp)
#define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL)
#define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : (((int)((a)->i) > (utarray_eltidx(a,e)+1)) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL))
#define utarray_prev(a,e) (((e)==NULL) ? utarray_back(a) : ((utarray_eltidx(a,e) > 0) ? _utarray_eltptr(a,utarray_eltidx(a,e)-1) : NULL))
#define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL)
#define utarray_eltidx(a,e) (((char*)(e) >= (char*)((a)->d)) ? (int)(((char*)(e) - (char*)((a)->d))/(a)->icd.sz) : -1)
/* last we pre-define a few icd for common utarrays of ints and strings */
static void utarray_str_cpy(void *dst, const void *src) {
char *const*_src = (char*const*)src, **_dst = (char**)dst;
*_dst = (*_src == NULL) ? NULL : strdup(*_src);
}
static void utarray_str_dtor(void *elt) {
char **eltc = (char**)elt;
if (*eltc) free(*eltc);
}
static const UT_icd ut_str_icd _UNUSED_ = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor};
static const UT_icd ut_int_icd _UNUSED_ = {sizeof(int),NULL,NULL,NULL};
static const UT_icd ut_ptr_icd _UNUSED_ = {sizeof(void*),NULL,NULL,NULL};
#endif /* UTARRAY_H */

919
common/uthash.h Normal file
View File

@ -0,0 +1,919 @@
/*
Copyright (c) 2003-2013, Troy D. Hanson http://uthash.sourceforge.net
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* $Id: uthash.h 2682 2012-11-23 22:04:22Z kaiwang27 $ */
#ifndef UTHASH_H
#define UTHASH_H
#include <string.h> /* memcmp,strlen */
#include <stddef.h> /* ptrdiff_t */
#include <stdlib.h> /* exit() */
/* These macros use decltype or the earlier __typeof GNU extension.
As decltype is only available in newer compilers (VS2010 or gcc 4.3+
when compiling c++ source) this code uses whatever method is needed
or, for VS2008 where neither is available, uses casting workarounds. */
#ifdef _MSC_VER /* MS compiler */
#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */
#define DECLTYPE(x) (decltype(x))
#else /* VS2008 or older (or VS2010 in C mode) */
#define NO_DECLTYPE
#define DECLTYPE(x)
#endif
#else /* GNU, Sun and other compilers */
#define DECLTYPE(x) (__typeof(x))
#endif
#ifdef NO_DECLTYPE
#define DECLTYPE_ASSIGN(dst,src) \
do { \
char **_da_dst = (char**)(&(dst)); \
*_da_dst = (char*)(src); \
} while(0)
#else
#define DECLTYPE_ASSIGN(dst,src) \
do { \
(dst) = DECLTYPE(dst)(src); \
} while(0)
#endif
/* a number of the hash function use uint32_t which isn't defined on win32 */
#ifdef _MSC_VER
typedef unsigned int uint32_t;
typedef unsigned char uint8_t;
#else
#include <inttypes.h> /* uint32_t */
#endif
#define UTHASH_VERSION 1.9.7
#ifndef uthash_fatal
#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */
#endif
#ifndef uthash_malloc
#define uthash_malloc(sz) malloc(sz) /* malloc fcn */
#endif
#ifndef uthash_free
#define uthash_free(ptr,sz) free(ptr) /* free fcn */
#endif
#ifndef uthash_noexpand_fyi
#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */
#endif
#ifndef uthash_expand_fyi
#define uthash_expand_fyi(tbl) /* can be defined to log expands */
#endif
/* initial number of buckets */
#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */
#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */
#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */
/* calculate the element whose hash handle address is hhe */
#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
#define HASH_FIND(hh,head,keyptr,keylen,out) \
do { \
unsigned _hf_bkt,_hf_hashv; \
out=NULL; \
if (head) { \
HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \
if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \
HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \
keyptr,keylen,out); \
} \
} \
} while (0)
#ifdef HASH_BLOOM
#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM)
#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0)
#define HASH_BLOOM_MAKE(tbl) \
do { \
(tbl)->bloom_nbits = HASH_BLOOM; \
(tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \
if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \
memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \
(tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \
} while (0)
#define HASH_BLOOM_FREE(tbl) \
do { \
uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \
} while (0)
#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8)))
#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8)))
#define HASH_BLOOM_ADD(tbl,hashv) \
HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
#define HASH_BLOOM_TEST(tbl,hashv) \
HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
#else
#define HASH_BLOOM_MAKE(tbl)
#define HASH_BLOOM_FREE(tbl)
#define HASH_BLOOM_ADD(tbl,hashv)
#define HASH_BLOOM_TEST(tbl,hashv) (1)
#endif
#define HASH_MAKE_TABLE(hh,head) \
do { \
(head)->hh.tbl = (UT_hash_table*)uthash_malloc( \
sizeof(UT_hash_table)); \
if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \
memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \
(head)->hh.tbl->tail = &((head)->hh); \
(head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \
(head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \
(head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \
(head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \
HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \
memset((head)->hh.tbl->buckets, 0, \
HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
HASH_BLOOM_MAKE((head)->hh.tbl); \
(head)->hh.tbl->signature = HASH_SIGNATURE; \
} while(0)
#define HASH_ADD(hh,head,fieldname,keylen_in,add) \
HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add)
#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \
do { \
unsigned _ha_bkt; \
(add)->hh.next = NULL; \
(add)->hh.key = (char*)keyptr; \
(add)->hh.keylen = (unsigned)keylen_in; \
if (!(head)) { \
head = (add); \
(head)->hh.prev = NULL; \
HASH_MAKE_TABLE(hh,head); \
} else { \
(head)->hh.tbl->tail->next = (add); \
(add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \
(head)->hh.tbl->tail = &((add)->hh); \
} \
(head)->hh.tbl->num_items++; \
(add)->hh.tbl = (head)->hh.tbl; \
HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \
(add)->hh.hashv, _ha_bkt); \
HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \
HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \
HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \
HASH_FSCK(hh,head); \
} while(0)
#define HASH_TO_BKT( hashv, num_bkts, bkt ) \
do { \
bkt = ((hashv) & ((num_bkts) - 1)); \
} while(0)
/* delete "delptr" from the hash table.
* "the usual" patch-up process for the app-order doubly-linked-list.
* The use of _hd_hh_del below deserves special explanation.
* These used to be expressed using (delptr) but that led to a bug
* if someone used the same symbol for the head and deletee, like
* HASH_DELETE(hh,users,users);
* We want that to work, but by changing the head (users) below
* we were forfeiting our ability to further refer to the deletee (users)
* in the patch-up process. Solution: use scratch space to
* copy the deletee pointer, then the latter references are via that
* scratch pointer rather than through the repointed (users) symbol.
*/
#define HASH_DELETE(hh,head,delptr) \
do { \
unsigned _hd_bkt; \
struct UT_hash_handle *_hd_hh_del; \
if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \
uthash_free((head)->hh.tbl->buckets, \
(head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
HASH_BLOOM_FREE((head)->hh.tbl); \
uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
head = NULL; \
} else { \
_hd_hh_del = &((delptr)->hh); \
if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \
(head)->hh.tbl->tail = \
(UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \
(head)->hh.tbl->hho); \
} \
if ((delptr)->hh.prev) { \
((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \
(head)->hh.tbl->hho))->next = (delptr)->hh.next; \
} else { \
DECLTYPE_ASSIGN(head,(delptr)->hh.next); \
} \
if (_hd_hh_del->next) { \
((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \
(head)->hh.tbl->hho))->prev = \
_hd_hh_del->prev; \
} \
HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \
(head)->hh.tbl->num_items--; \
} \
HASH_FSCK(hh,head); \
} while (0)
/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
#define HASH_FIND_STR(head,findstr,out) \
HASH_FIND(hh,head,findstr,strlen(findstr),out)
#define HASH_ADD_STR(head,strfield,add) \
HASH_ADD(hh,head,strfield,strlen(add->strfield),add)
#define HASH_FIND_INT(head,findint,out) \
HASH_FIND(hh,head,findint,sizeof(int),out)
#define HASH_ADD_INT(head,intfield,add) \
HASH_ADD(hh,head,intfield,sizeof(int),add)
#define HASH_FIND_PTR(head,findptr,out) \
HASH_FIND(hh,head,findptr,sizeof(void *),out)
#define HASH_ADD_PTR(head,ptrfield,add) \
HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
#define HASH_DEL(head,delptr) \
HASH_DELETE(hh,head,delptr)
/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
* This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
*/
#ifdef HASH_DEBUG
#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
#define HASH_FSCK(hh,head) \
do { \
unsigned _bkt_i; \
unsigned _count, _bkt_count; \
char *_prev; \
struct UT_hash_handle *_thh; \
if (head) { \
_count = 0; \
for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \
_bkt_count = 0; \
_thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \
_prev = NULL; \
while (_thh) { \
if (_prev != (char*)(_thh->hh_prev)) { \
HASH_OOPS("invalid hh_prev %p, actual %p\n", \
_thh->hh_prev, _prev ); \
} \
_bkt_count++; \
_prev = (char*)(_thh); \
_thh = _thh->hh_next; \
} \
_count += _bkt_count; \
if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \
HASH_OOPS("invalid bucket count %d, actual %d\n", \
(head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \
} \
} \
if (_count != (head)->hh.tbl->num_items) { \
HASH_OOPS("invalid hh item count %d, actual %d\n", \
(head)->hh.tbl->num_items, _count ); \
} \
/* traverse hh in app order; check next/prev integrity, count */ \
_count = 0; \
_prev = NULL; \
_thh = &(head)->hh; \
while (_thh) { \
_count++; \
if (_prev !=(char*)(_thh->prev)) { \
HASH_OOPS("invalid prev %p, actual %p\n", \
_thh->prev, _prev ); \
} \
_prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \
_thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \
(head)->hh.tbl->hho) : NULL ); \
} \
if (_count != (head)->hh.tbl->num_items) { \
HASH_OOPS("invalid app item count %d, actual %d\n", \
(head)->hh.tbl->num_items, _count ); \
} \
} \
} while (0)
#else
#define HASH_FSCK(hh,head)
#endif
/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
* the descriptor to which this macro is defined for tuning the hash function.
* The app can #include <unistd.h> to get the prototype for write(2). */
#ifdef HASH_EMIT_KEYS
#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \
do { \
unsigned _klen = fieldlen; \
write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \
write(HASH_EMIT_KEYS, keyptr, fieldlen); \
} while (0)
#else
#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
#endif
/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
#ifdef HASH_FUNCTION
#define HASH_FCN HASH_FUNCTION
#else
#define HASH_FCN HASH_JEN
#endif
/* The Bernstein hash function, used in Perl prior to v5.6 */
#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned _hb_keylen=keylen; \
char *_hb_key=(char*)(key); \
(hashv) = 0; \
while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \
bkt = (hashv) & (num_bkts-1); \
} while (0)
/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
* http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned _sx_i; \
char *_hs_key=(char*)(key); \
hashv = 0; \
for(_sx_i=0; _sx_i < keylen; _sx_i++) \
hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \
bkt = hashv & (num_bkts-1); \
} while (0)
#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned _fn_i; \
char *_hf_key=(char*)(key); \
hashv = 2166136261UL; \
for(_fn_i=0; _fn_i < keylen; _fn_i++) \
hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \
bkt = hashv & (num_bkts-1); \
} while(0)
#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned _ho_i; \
char *_ho_key=(char*)(key); \
hashv = 0; \
for(_ho_i=0; _ho_i < keylen; _ho_i++) { \
hashv += _ho_key[_ho_i]; \
hashv += (hashv << 10); \
hashv ^= (hashv >> 6); \
} \
hashv += (hashv << 3); \
hashv ^= (hashv >> 11); \
hashv += (hashv << 15); \
bkt = hashv & (num_bkts-1); \
} while(0)
#define HASH_JEN_MIX(a,b,c) \
do { \
a -= b; a -= c; a ^= ( c >> 13 ); \
b -= c; b -= a; b ^= ( a << 8 ); \
c -= a; c -= b; c ^= ( b >> 13 ); \
a -= b; a -= c; a ^= ( c >> 12 ); \
b -= c; b -= a; b ^= ( a << 16 ); \
c -= a; c -= b; c ^= ( b >> 5 ); \
a -= b; a -= c; a ^= ( c >> 3 ); \
b -= c; b -= a; b ^= ( a << 10 ); \
c -= a; c -= b; c ^= ( b >> 15 ); \
} while (0)
#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned _hj_i,_hj_j,_hj_k; \
char *_hj_key=(char*)(key); \
hashv = 0xfeedbeef; \
_hj_i = _hj_j = 0x9e3779b9; \
_hj_k = (unsigned)keylen; \
while (_hj_k >= 12) { \
_hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \
+ ( (unsigned)_hj_key[2] << 16 ) \
+ ( (unsigned)_hj_key[3] << 24 ) ); \
_hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \
+ ( (unsigned)_hj_key[6] << 16 ) \
+ ( (unsigned)_hj_key[7] << 24 ) ); \
hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \
+ ( (unsigned)_hj_key[10] << 16 ) \
+ ( (unsigned)_hj_key[11] << 24 ) ); \
\
HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
\
_hj_key += 12; \
_hj_k -= 12; \
} \
hashv += keylen; \
switch ( _hj_k ) { \
case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \
case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \
case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \
case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \
case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \
case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \
case 5: _hj_j += _hj_key[4]; \
case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \
case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \
case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \
case 1: _hj_i += _hj_key[0]; \
} \
HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
bkt = hashv & (num_bkts-1); \
} while(0)
/* The Paul Hsieh hash function */
#undef get16bits
#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
|| defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
#define get16bits(d) (*((const uint16_t *) (d)))
#endif
#if !defined (get16bits)
#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
+(uint32_t)(((const uint8_t *)(d))[0]) )
#endif
#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \
do { \
char *_sfh_key=(char*)(key); \
uint32_t _sfh_tmp, _sfh_len = keylen; \
\
int _sfh_rem = _sfh_len & 3; \
_sfh_len >>= 2; \
hashv = 0xcafebabe; \
\
/* Main loop */ \
for (;_sfh_len > 0; _sfh_len--) { \
hashv += get16bits (_sfh_key); \
_sfh_tmp = (get16bits (_sfh_key+2) << 11) ^ hashv; \
hashv = (hashv << 16) ^ _sfh_tmp; \
_sfh_key += 2*sizeof (uint16_t); \
hashv += hashv >> 11; \
} \
\
/* Handle end cases */ \
switch (_sfh_rem) { \
case 3: hashv += get16bits (_sfh_key); \
hashv ^= hashv << 16; \
hashv ^= _sfh_key[sizeof (uint16_t)] << 18; \
hashv += hashv >> 11; \
break; \
case 2: hashv += get16bits (_sfh_key); \
hashv ^= hashv << 11; \
hashv += hashv >> 17; \
break; \
case 1: hashv += *_sfh_key; \
hashv ^= hashv << 10; \
hashv += hashv >> 1; \
} \
\
/* Force "avalanching" of final 127 bits */ \
hashv ^= hashv << 3; \
hashv += hashv >> 5; \
hashv ^= hashv << 4; \
hashv += hashv >> 17; \
hashv ^= hashv << 25; \
hashv += hashv >> 6; \
bkt = hashv & (num_bkts-1); \
} while(0)
#ifdef HASH_USING_NO_STRICT_ALIASING
/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads.
* For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error.
* MurmurHash uses the faster approach only on CPU's where we know it's safe.
*
* Note the preprocessor built-in defines can be emitted using:
*
* gcc -m64 -dM -E - < /dev/null (on gcc)
* cc -## a.c (where a.c is a simple test file) (Sun Studio)
*/
#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86))
#define MUR_GETBLOCK(p,i) p[i]
#else /* non intel */
#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 0x3) == 0)
#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 0x3) == 1)
#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 0x3) == 2)
#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 0x3) == 3)
#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL))
#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__))
#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24))
#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16))
#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8))
#else /* assume little endian non-intel */
#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24))
#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16))
#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8))
#endif
#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \
(MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \
(MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \
MUR_ONE_THREE(p))))
#endif
#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r))))
#define MUR_FMIX(_h) \
do { \
_h ^= _h >> 16; \
_h *= 0x85ebca6b; \
_h ^= _h >> 13; \
_h *= 0xc2b2ae35l; \
_h ^= _h >> 16; \
} while(0)
#define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \
do { \
const uint8_t *_mur_data = (const uint8_t*)(key); \
const int _mur_nblocks = (keylen) / 4; \
uint32_t _mur_h1 = 0xf88D5353; \
uint32_t _mur_c1 = 0xcc9e2d51; \
uint32_t _mur_c2 = 0x1b873593; \
uint32_t _mur_k1 = 0; \
const uint8_t *_mur_tail; \
const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+_mur_nblocks*4); \
int _mur_i; \
for(_mur_i = -_mur_nblocks; _mur_i; _mur_i++) { \
_mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \
_mur_k1 *= _mur_c1; \
_mur_k1 = MUR_ROTL32(_mur_k1,15); \
_mur_k1 *= _mur_c2; \
\
_mur_h1 ^= _mur_k1; \
_mur_h1 = MUR_ROTL32(_mur_h1,13); \
_mur_h1 = _mur_h1*5+0xe6546b64; \
} \
_mur_tail = (const uint8_t*)(_mur_data + _mur_nblocks*4); \
_mur_k1=0; \
switch((keylen) & 3) { \
case 3: _mur_k1 ^= _mur_tail[2] << 16; \
case 2: _mur_k1 ^= _mur_tail[1] << 8; \
case 1: _mur_k1 ^= _mur_tail[0]; \
_mur_k1 *= _mur_c1; \
_mur_k1 = MUR_ROTL32(_mur_k1,15); \
_mur_k1 *= _mur_c2; \
_mur_h1 ^= _mur_k1; \
} \
_mur_h1 ^= (keylen); \
MUR_FMIX(_mur_h1); \
hashv = _mur_h1; \
bkt = hashv & (num_bkts-1); \
} while(0)
#endif /* HASH_USING_NO_STRICT_ALIASING */
/* key comparison function; return 0 if keys equal */
#define HASH_KEYCMP(a,b,len) memcmp(a,b,len)
/* iterate over items in a known bucket to find desired item */
#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \
do { \
if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \
else out=NULL; \
while (out) { \
if ((out)->hh.keylen == keylen_in) { \
if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) break; \
} \
if ((out)->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); \
else out = NULL; \
} \
} while(0)
/* add an item to a bucket */
#define HASH_ADD_TO_BKT(head,addhh) \
do { \
head.count++; \
(addhh)->hh_next = head.hh_head; \
(addhh)->hh_prev = NULL; \
if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \
(head).hh_head=addhh; \
if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \
&& (addhh)->tbl->noexpand != 1) { \
HASH_EXPAND_BUCKETS((addhh)->tbl); \
} \
} while(0)
/* remove an item from a given bucket */
#define HASH_DEL_IN_BKT(hh,head,hh_del) \
(head).count--; \
if ((head).hh_head == hh_del) { \
(head).hh_head = hh_del->hh_next; \
} \
if (hh_del->hh_prev) { \
hh_del->hh_prev->hh_next = hh_del->hh_next; \
} \
if (hh_del->hh_next) { \
hh_del->hh_next->hh_prev = hh_del->hh_prev; \
}
/* Bucket expansion has the effect of doubling the number of buckets
* and redistributing the items into the new buckets. Ideally the
* items will distribute more or less evenly into the new buckets
* (the extent to which this is true is a measure of the quality of
* the hash function as it applies to the key domain).
*
* With the items distributed into more buckets, the chain length
* (item count) in each bucket is reduced. Thus by expanding buckets
* the hash keeps a bound on the chain length. This bounded chain
* length is the essence of how a hash provides constant time lookup.
*
* The calculation of tbl->ideal_chain_maxlen below deserves some
* explanation. First, keep in mind that we're calculating the ideal
* maximum chain length based on the *new* (doubled) bucket count.
* In fractions this is just n/b (n=number of items,b=new num buckets).
* Since the ideal chain length is an integer, we want to calculate
* ceil(n/b). We don't depend on floating point arithmetic in this
* hash, so to calculate ceil(n/b) with integers we could write
*
* ceil(n/b) = (n/b) + ((n%b)?1:0)
*
* and in fact a previous version of this hash did just that.
* But now we have improved things a bit by recognizing that b is
* always a power of two. We keep its base 2 log handy (call it lb),
* so now we can write this with a bit shift and logical AND:
*
* ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
*
*/
#define HASH_EXPAND_BUCKETS(tbl) \
do { \
unsigned _he_bkt; \
unsigned _he_bkt_i; \
struct UT_hash_handle *_he_thh, *_he_hh_nxt; \
UT_hash_bucket *_he_new_buckets, *_he_newbkt; \
_he_new_buckets = (UT_hash_bucket*)uthash_malloc( \
2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \
memset(_he_new_buckets, 0, \
2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
tbl->ideal_chain_maxlen = \
(tbl->num_items >> (tbl->log2_num_buckets+1)) + \
((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \
tbl->nonideal_items = 0; \
for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \
{ \
_he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \
while (_he_thh) { \
_he_hh_nxt = _he_thh->hh_next; \
HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \
_he_newbkt = &(_he_new_buckets[ _he_bkt ]); \
if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \
tbl->nonideal_items++; \
_he_newbkt->expand_mult = _he_newbkt->count / \
tbl->ideal_chain_maxlen; \
} \
_he_thh->hh_prev = NULL; \
_he_thh->hh_next = _he_newbkt->hh_head; \
if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \
_he_thh; \
_he_newbkt->hh_head = _he_thh; \
_he_thh = _he_hh_nxt; \
} \
} \
uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
tbl->num_buckets *= 2; \
tbl->log2_num_buckets++; \
tbl->buckets = _he_new_buckets; \
tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \
(tbl->ineff_expands+1) : 0; \
if (tbl->ineff_expands > 1) { \
tbl->noexpand=1; \
uthash_noexpand_fyi(tbl); \
} \
uthash_expand_fyi(tbl); \
} while(0)
/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
/* Note that HASH_SORT assumes the hash handle name to be hh.
* HASH_SRT was added to allow the hash handle name to be passed in. */
#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
#define HASH_SRT(hh,head,cmpfcn) \
do { \
unsigned _hs_i; \
unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \
struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \
if (head) { \
_hs_insize = 1; \
_hs_looping = 1; \
_hs_list = &((head)->hh); \
while (_hs_looping) { \
_hs_p = _hs_list; \
_hs_list = NULL; \
_hs_tail = NULL; \
_hs_nmerges = 0; \
while (_hs_p) { \
_hs_nmerges++; \
_hs_q = _hs_p; \
_hs_psize = 0; \
for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \
_hs_psize++; \
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
((void*)((char*)(_hs_q->next) + \
(head)->hh.tbl->hho)) : NULL); \
if (! (_hs_q) ) break; \
} \
_hs_qsize = _hs_insize; \
while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \
if (_hs_psize == 0) { \
_hs_e = _hs_q; \
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
((void*)((char*)(_hs_q->next) + \
(head)->hh.tbl->hho)) : NULL); \
_hs_qsize--; \
} else if ( (_hs_qsize == 0) || !(_hs_q) ) { \
_hs_e = _hs_p; \
_hs_p = (UT_hash_handle*)((_hs_p->next) ? \
((void*)((char*)(_hs_p->next) + \
(head)->hh.tbl->hho)) : NULL); \
_hs_psize--; \
} else if (( \
cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \
DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \
) <= 0) { \
_hs_e = _hs_p; \
_hs_p = (UT_hash_handle*)((_hs_p->next) ? \
((void*)((char*)(_hs_p->next) + \
(head)->hh.tbl->hho)) : NULL); \
_hs_psize--; \
} else { \
_hs_e = _hs_q; \
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
((void*)((char*)(_hs_q->next) + \
(head)->hh.tbl->hho)) : NULL); \
_hs_qsize--; \
} \
if ( _hs_tail ) { \
_hs_tail->next = ((_hs_e) ? \
ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \
} else { \
_hs_list = _hs_e; \
} \
_hs_e->prev = ((_hs_tail) ? \
ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \
_hs_tail = _hs_e; \
} \
_hs_p = _hs_q; \
} \
_hs_tail->next = NULL; \
if ( _hs_nmerges <= 1 ) { \
_hs_looping=0; \
(head)->hh.tbl->tail = _hs_tail; \
DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \
} \
_hs_insize *= 2; \
} \
HASH_FSCK(hh,head); \
} \
} while (0)
/* This function selects items from one hash into another hash.
* The end result is that the selected items have dual presence
* in both hashes. There is no copy of the items made; rather
* they are added into the new hash through a secondary hash
* hash handle that must be present in the structure. */
#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \
do { \
unsigned _src_bkt, _dst_bkt; \
void *_last_elt=NULL, *_elt; \
UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \
ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \
if (src) { \
for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \
for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \
_src_hh; \
_src_hh = _src_hh->hh_next) { \
_elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \
if (cond(_elt)) { \
_dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \
_dst_hh->key = _src_hh->key; \
_dst_hh->keylen = _src_hh->keylen; \
_dst_hh->hashv = _src_hh->hashv; \
_dst_hh->prev = _last_elt; \
_dst_hh->next = NULL; \
if (_last_elt_hh) { _last_elt_hh->next = _elt; } \
if (!dst) { \
DECLTYPE_ASSIGN(dst,_elt); \
HASH_MAKE_TABLE(hh_dst,dst); \
} else { \
_dst_hh->tbl = (dst)->hh_dst.tbl; \
} \
HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \
HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \
(dst)->hh_dst.tbl->num_items++; \
_last_elt = _elt; \
_last_elt_hh = _dst_hh; \
} \
} \
} \
} \
HASH_FSCK(hh_dst,dst); \
} while (0)
#define HASH_CLEAR(hh,head) \
do { \
if (head) { \
uthash_free((head)->hh.tbl->buckets, \
(head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \
HASH_BLOOM_FREE((head)->hh.tbl); \
uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
(head)=NULL; \
} \
} while(0)
#ifdef NO_DECLTYPE
#define HASH_ITER(hh,head,el,tmp) \
for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \
el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL))
#else
#define HASH_ITER(hh,head,el,tmp) \
for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \
el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL))
#endif
/* obtain a count of items in the hash */
#define HASH_COUNT(head) HASH_CNT(hh,head)
#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0)
typedef struct UT_hash_bucket {
struct UT_hash_handle *hh_head;
unsigned count;
/* expand_mult is normally set to 0. In this situation, the max chain length
* threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
* the bucket's chain exceeds this length, bucket expansion is triggered).
* However, setting expand_mult to a non-zero value delays bucket expansion
* (that would be triggered by additions to this particular bucket)
* until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
* (The multiplier is simply expand_mult+1). The whole idea of this
* multiplier is to reduce bucket expansions, since they are expensive, in
* situations where we know that a particular bucket tends to be overused.
* It is better to let its chain length grow to a longer yet-still-bounded
* value, than to do an O(n) bucket expansion too often.
*/
unsigned expand_mult;
} UT_hash_bucket;
/* random signature used only to find hash tables in external analysis */
#define HASH_SIGNATURE 0xa0111fe1
#define HASH_BLOOM_SIGNATURE 0xb12220f2
typedef struct UT_hash_table {
UT_hash_bucket *buckets;
unsigned num_buckets, log2_num_buckets;
unsigned num_items;
struct UT_hash_handle *tail; /* tail hh in app order, for fast append */
ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
/* in an ideal situation (all buckets used equally), no bucket would have
* more than ceil(#items/#buckets) items. that's the ideal chain length. */
unsigned ideal_chain_maxlen;
/* nonideal_items is the number of items in the hash whose chain position
* exceeds the ideal chain maxlen. these items pay the penalty for an uneven
* hash distribution; reaching them in a chain traversal takes >ideal steps */
unsigned nonideal_items;
/* ineffective expands occur when a bucket doubling was performed, but
* afterward, more than half the items in the hash had nonideal chain
* positions. If this happens on two consecutive expansions we inhibit any
* further expansion, as it's not helping; this happens when the hash
* function isn't a good fit for the key domain. When expansion is inhibited
* the hash will still work, albeit no longer in constant time. */
unsigned ineff_expands, noexpand;
uint32_t signature; /* used only to find hash tables in external analysis */
#ifdef HASH_BLOOM
uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
uint8_t *bloom_bv;
char bloom_nbits;
#endif
} UT_hash_table;
typedef struct UT_hash_handle {
struct UT_hash_table *tbl;
void *prev; /* prev element in app order */
void *next; /* next element in app order */
struct UT_hash_handle *hh_prev; /* previous hh in bucket order */
struct UT_hash_handle *hh_next; /* next hh in bucket order */
void *key; /* ptr to enclosing struct's key */
unsigned keylen; /* enclosing struct's key len */
unsigned hashv; /* result of hash-fcn(key) */
} UT_hash_handle;
#endif /* UTHASH_H */

15
cxxfilt/Makefile Normal file
View File

@ -0,0 +1,15 @@
# $Id: Makefile 2066 2011-10-26 15:40:28Z jkoshy $
TOP= ..
PROG= c++filt
SRCS= cxxfilt.c
WARNS?= 6
DPADD= ${LIBELFTC} ${LIBELF}
LDADD= -lelftc -lelf
MAN1= c++filt.1
.include "${TOP}/mk/elftoolchain.prog.mk"

109
cxxfilt/c++filt.1 Normal file
View File

@ -0,0 +1,109 @@
.\" Copyright (c) 2009-2011 Joseph Koshy <jkoshy@users.sourceforge.net>
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer
.\" in this position and unchanged.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\" $Id: c++filt.1 2175 2011-11-16 05:51:49Z jkoshy $
.\"
.Dd August 24, 2011
.Os
.Dt C++FILT 1
.Sh NAME
.Nm c++filt
.Nd decode C++ symbols
.Sh SYNOPSIS
.Nm
.Op Fl -help
.Op Fl _ | Fl -strip-underscores
.Op Fl n | Fl -no-strip-underscores
.Op Fl p | Fl -no-params
.Op Fl s Ar scheme | Fl -format Ns = Ns Ar scheme
.Op Fl V | Fl -version
.Op Ar encoded-names ...
.Sh DESCRIPTION
The
.Nm
utility translates encoded C++ symbol names to human-readable form.
.Pp
The
.Nm
utility has two operating modes.
.Bl -bullet
.It
If arguments
.Ar encoded-names
are not specified, then
.Nm
will act as a filter, reading from standard input
and writing to standard output.
.It
If arguments
.Ar encoded-names
are specified, then
.Nm
will decode each such argument in turn, writing its decoded form
to standard output.
.El
.Pp
The
.Nm
utility recognizes the following options:
.Bl -tag -width indent
.It Fl -help
Print a help message and exit.
.It Fl _ | Fl -strip-underscores
Remove a leading underscore from symbol names prior to decoding them.
.It Fl n | Fl -no-strip-underscores
Do not remove leading underscores from names.
.It Fl p | Fl -no-params
This option is recognized but ignored.
.It Fl s Ar scheme | Fl -format Ns = Ns Ar scheme
Select the encoding scheme to use.
Argument
.Ar scheme
can be one of the following:
.Bl -tag -width "gnu-v5"
.It Ar arm
Use the encoding scheme specified by the C++ Annotated Reference Manual.
.It Ar auto
Guess the encoding scheme from the input.
.It Ar gnu
Use the encoding scheme used by the GNU C++ compiler.
.It Ar gnu-v3
Use the encoding scheme used by the GNU C++ compiler, version 3.
.El
.It Fl V | Fl -version
Print a version identifier for
.Nm
and exit.
.El
.Sh EXIT STATUS
.Ex -std
.Sh SEE ALSO
.Xr nm 1 ,
.Xr strip 1 ,
.Xr elftc_demangle 3
.Sh AUTHORS
The
.Nm
utility was written by
.An "Kai Wang" Aq kaiwang27@users.sourceforge.net .

224
cxxfilt/cxxfilt.c Normal file
View File

@ -0,0 +1,224 @@
/*-
* Copyright (c) 2009 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <ctype.h>
#include <err.h>
#include <getopt.h>
#include <libelftc.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "_elftc.h"
ELFTC_VCSID("$Id: cxxfilt.c 2185 2011-11-19 16:07:16Z jkoshy $");
#define STRBUFSZ 8192
static int stripus = 0;
static int noparam = 0;
static int format = 0;
enum options
{
OPTION_HELP,
OPTION_VERSION
};
static struct option longopts[] =
{
{"format", required_argument, NULL, 's'},
{"help", no_argument, NULL, OPTION_HELP},
{"no-params", no_argument, NULL, 'p'},
{"no-strip-underscores", no_argument, NULL, 'n'},
{"strip-underscores", no_argument, NULL, '_'},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};
static struct {
const char *fname;
int fvalue;
} flist[] = {
{"auto", 0},
{"arm", ELFTC_DEM_ARM},
{"gnu", ELFTC_DEM_GNU2},
{"gnu-v3", ELFTC_DEM_GNU3}
};
#define USAGE_MESSAGE "\
Usage: %s [options] [encoded-names...]\n\
Translate C++ symbol names to human-readable form.\n\n\
Options:\n\
-_ | --strip-underscores Remove leading underscores prior to decoding.\n\
-n | --no-strip-underscores Do not remove leading underscores.\n\
-p | --no-params (Accepted but ignored).\n\
-s SCHEME | --format=SCHEME Select the encoding scheme to use.\n\
Valid schemes are: 'arm', 'auto', 'gnu' and\n\
'gnu-v3'.\n\
--help Print a help message.\n\
--version Print a version identifier and exit.\n"
static void
usage(void)
{
(void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
exit(1);
}
static void
version(void)
{
fprintf(stderr, "%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version());
exit(0);
}
static int
find_format(const char *fstr)
{
int i;
for (i = 0; (size_t) i < sizeof(flist) / sizeof(flist[0]); i++) {
if (!strcmp(fstr, flist[i].fname))
return (flist[i].fvalue);
}
return (-1);
}
static char *
demangle(char *name, int strict, int *pos)
{
static char dem[STRBUFSZ];
char nb[STRBUFSZ];
int p, t;
if (stripus && *name == '_') {
strncpy(nb, name + 1, sizeof(nb) - 1);
t = 1;
} else {
strncpy(nb, name, sizeof(nb) - 1);
t = 0;
}
nb[sizeof(nb) - 1] = '\0';
p = strlen(nb);
if (p <= 0)
return NULL;
while (elftc_demangle(nb, dem, sizeof(dem), format) < 0) {
if (!strict && p > 1) {
nb[--p] = '\0';
continue;
} else
return (NULL);
}
if (pos != NULL)
*pos = t ? p + 1 : p;
return (dem);
}
int
main(int argc, char **argv)
{
char *dem, buf[STRBUFSZ];
int c, i, p, s, opt;
while ((opt = getopt_long(argc, argv, "_nps:V", longopts, NULL)) !=
-1) {
switch (opt) {
case '_':
stripus = 1;
break;
case 'n':
stripus = 0;
break;
case 'p':
noparam = 1;
break;
case 's':
if ((format = find_format(optarg)) < 0)
errx(EXIT_FAILURE, "unsupported format: %s",
optarg);
break;
case 'V':
version();
/* NOT REACHED */
case OPTION_HELP:
default:
usage();
/* NOT REACHED */
}
}
argv += optind;
argc -= optind;
if (*argv != NULL) {
for (i = 0; i < argc; i++) {
if ((dem = demangle(argv[i], 1, NULL)) == NULL)
fprintf(stderr, "Failed: %s\n", argv[i]);
else
printf("%s\n", dem);
}
} else {
p = 0;
for (;;) {
c = fgetc(stdin);
if (c == EOF || !isprint(c) || strchr(" \t\n", c)) {
if (p > 0) {
buf[p] = '\0';
if ((dem = demangle(buf, 0, &s)) ==
NULL)
printf("%s", buf);
else {
printf("%s", dem);
for (i = s; i < p; i++)
putchar(buf[i]);
}
p = 0;
}
if (c == EOF)
break;
if (isprint(c) || strchr(" \t\n", c))
putchar(c);
} else {
if ((size_t) p >= sizeof(buf) - 1)
warnx("buffer overflowed");
else
buf[p++] = c;
}
}
}
exit(0);
}

7
documentation/Makefile Normal file
View File

@ -0,0 +1,7 @@
# $Id: Makefile 2139 2011-11-10 14:23:13Z jkoshy $
TOP= ..
SUBDIR= libelf-by-example
.include "${TOP}/mk/elftoolchain.subdir.mk"

View File

@ -0,0 +1,28 @@
#
# Libelf by Example
#
# $Id: Makefile 2441 2012-02-21 05:46:43Z jkoshy $
TOP = ../..
DOC= libelf-by-example
SRCS= libelf-by-example.tex prog1.txt prog2.txt prog3.txt \
prog4.txt prog5.txt prog6.txt
.include "${TOP}/mk/elftoolchain.tex.mk"
.if ${OS_HOST} == "Linux"
EXTRA_LIBS= -lbsd
.endif
check-example-syntax: .PHONY
.for f in ${SRCS:Mprog*}
@c=$$(basename ${f} .txt).c; sed -e 's/@[^@]*@//' \
${.CURDIR}/${f} > ${.OBJDIR}/$${c}; \
echo -n $${c} ' '; cc -I${.CURDIR}/${TOP}/common \
-I${.CURDIR}/${TOP}/libelf ${.OBJDIR}/$${c} \
-L${.CURDIR}/${TOP}/libelf -lelf ${EXTRA_LIBS} && \
rm ${.OBJDIR}/$${c} a.out
.endfor
@echo

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,58 @@
/*
* Getting started with libelf.
*
* $Id: prog1.txt 2133 2011-11-10 08:28:22Z jkoshy $
*/
#include <err.h>
#include <fcntl.h>
#include <libelf.h> @\co{1}@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main(int argc, char **argv)
{
int fd;
Elf *e; @\co{2}@
char *k;
Elf_Kind ek; @\co{3}@
if (argc != 2)
errx(EXIT_FAILURE, "usage: %s file-name", argv[0]);
if (elf_version(EV_CURRENT) == EV_NONE) @\co{4}@
errx(EXIT_FAILURE, "ELF library initialization "
"failed: %s", elf_errmsg(-1));
if ((fd = open(argv[1], O_RDONLY, 0)) < 0)
err(EXIT_FAILURE, "open \%s\" failed", argv[1]);
if ((e = elf_begin(fd, ELF_C_READ@\co{5}@, NULL)) == NULL)
errx(EXIT_FAILURE, "elf_begin() failed: %s.",
elf_errmsg(-1)); @\co{6}@
ek = elf_kind(e); @\co{7}@
switch (ek) {
case ELF_K_AR:
k = "ar(1) archive";
break;
case ELF_K_ELF:
k = "elf object";
break;
case ELF_K_NONE:
k = "data";
break;
default:
k = "unrecognized";
}
(void) printf("%s: %s\n", argv[1], k);
(void) elf_end(e); @\co{8}@
(void) close(fd);
exit(EXIT_SUCCESS);
}

View File

@ -0,0 +1,102 @@
/*
* Print the ELF Executable Header from an ELF object.
*
* $Id: prog2.txt 2133 2011-11-10 08:28:22Z jkoshy $
*/
#include <err.h>
#include <fcntl.h>
#include <gelf.h> @\co{1}@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <vis.h>
int
main(int argc, char **argv)
{
int i, fd;
Elf *e;
char *id, bytes[5];
size_t n;
GElf_Ehdr ehdr; @\co{2}@
if (argc != 2)
errx(EXIT_FAILURE, "usage: %s file-name", argv[0]);
if (elf_version(EV_CURRENT) == EV_NONE)
errx(EXIT_FAILURE, "ELF library initialization "
"failed: %s", elf_errmsg(-1));
if ((fd = open(argv[1], O_RDONLY, 0)) < 0)
err(EXIT_FAILURE, "open \"%s\" failed", argv[1]);
if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
errx(EXIT_FAILURE, "elf_begin() failed: %s.",
elf_errmsg(-1));
if (elf_kind(e) != ELF_K_ELF)
errx(EXIT_FAILURE, "\"%s\" is not an ELF object.",
argv[1]);
if (gelf_getehdr(e, &ehdr) == NULL) @\co{3}@
errx(EXIT_FAILURE, "getehdr() failed: %s.",
elf_errmsg(-1));
if ((i = gelf_getclass(e)) == ELFCLASSNONE) @\co{4}@
errx(EXIT_FAILURE, "getclass() failed: %s.",
elf_errmsg(-1));
(void) printf("%s: %d-bit ELF object\n", argv[1],
i == ELFCLASS32 ? 32 : 64);
if ((id = elf_getident(e, NULL)) == NULL) @\co{5}@
errx(EXIT_FAILURE, "getident() failed: %s.",
elf_errmsg(-1));
(void) printf("%3s e_ident[0..%1d] %7s", " ",
EI_ABIVERSION, " ");
for (i = 0; i <= EI_ABIVERSION; i++) {
(void) vis(bytes, id[i], VIS_WHITE, 0);
(void) printf(" ['%s' %X]", bytes, id[i]);
}
(void) printf("\n");
#define PRINT_FMT " %-20s 0x%jx\n"
#define PRINT_FIELD(N) do { \
(void) printf(PRINT_FMT, #N, (uintmax_t) ehdr.N); \
} while (0)
PRINT_FIELD(e_type); @\co{6}@
PRINT_FIELD(e_machine);
PRINT_FIELD(e_version);
PRINT_FIELD(e_entry);
PRINT_FIELD(e_phoff);
PRINT_FIELD(e_shoff);
PRINT_FIELD(e_flags);
PRINT_FIELD(e_ehsize);
PRINT_FIELD(e_phentsize);
PRINT_FIELD(e_shentsize);
if (elf_getshdrnum(e, &n) != 0) @\co{7}@
errx(EXIT_FAILURE, "getshdrnum() failed: %s.",
elf_errmsg(-1));
(void) printf(PRINT_FMT, "(shnum)", (uintmax_t) n);
if (elf_getshdrstrndx(e, &n) != 0) @\co{8}@
errx(EXIT_FAILURE, "getshdrstrndx() failed: %s.",
elf_errmsg(-1));
(void) printf(PRINT_FMT, "(shstrndx)", (uintmax_t) n);
if (elf_getphdrnum(e, &n) != 0) @\co{9}@
errx(EXIT_FAILURE, "getphdrnum() failed: %s.",
elf_errmsg(-1));
(void) printf(PRINT_FMT, "(phnum)", (uintmax_t) n);
(void) elf_end(e);
(void) close(fd);
exit(EXIT_SUCCESS);
}

View File

@ -0,0 +1,102 @@
/*
* Print the ELF Program Header Table in an ELF object.
*
* $Id: prog3.txt 2133 2011-11-10 08:28:22Z jkoshy $
*/
#include <err.h>
#include <fcntl.h>
#include <gelf.h> @\co{1}@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <vis.h>
void
print_ptype(size_t pt) @\co{7}@
{
char *s;
#define C(V) case PT_##V: s = #V; break
switch (pt) {
C(NULL); C(LOAD); C(DYNAMIC);
C(INTERP); C(NOTE); C(SHLIB);
C(PHDR); C(TLS); C(SUNW_UNWIND);
C(SUNWBSS); C(SUNWSTACK); C(SUNWDTRACE);
C(SUNWCAP);
default:
s = "unknown";
break;
}
(void) printf(" \"%s\"", s);
#undef C
}
int
main(int argc, char **argv)
{
int i, fd;
Elf *e;
char *id, bytes[5];
size_t n;
GElf_Phdr phdr; @\co{2}@
if (argc != 2)
errx(EXIT_FAILURE, "usage: %s file-name", argv[0]);
if (elf_version(EV_CURRENT) == EV_NONE)
errx(EXIT_FAILURE, "ELF library initialization "
"failed: %s", elf_errmsg(-1));
if ((fd = open(argv[1], O_RDONLY, 0)) < 0)
err(EXIT_FAILURE, "open \"%s\" failed", argv[1]);
if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
errx(EXIT_FAILURE, "elf_begin() failed: %s.",
elf_errmsg(-1));
if (elf_kind(e) != ELF_K_ELF)
errx(EXIT_FAILURE, "\"%s\" is not an ELF object.",
argv[1]);
if (elf_getphdrnum(e, &n) != 0) @\co{3}@
errx(EXIT_FAILURE, "elf_getphdrnum() failed: %s.",
elf_errmsg(-1));
for (i = 0; i < n; i++) { @\co{4}@
if (gelf_getphdr(e, i, &phdr) != &phdr) @\co{5}@
errx(EXIT_FAILURE, "getphdr() failed: %s.",
elf_errmsg(-1));
(void) printf("PHDR %d:\n", i);
#define PRINT_FMT " %-20s 0x%jx"
#define PRINT_FIELD(N) do { \
(void) printf(PRINT_FMT, #N, (uintmax_t) phdr.N); \
} while (0)
#define NL() do { (void) printf("\n"); } while (0)
PRINT_FIELD(p_type); @\co{6}@
print_ptype(phdr.p_type); NL();
PRINT_FIELD(p_offset); NL();
PRINT_FIELD(p_vaddr); NL();
PRINT_FIELD(p_paddr); NL();
PRINT_FIELD(p_filesz); NL();
PRINT_FIELD(p_memsz); NL();
PRINT_FIELD(p_flags);
(void) printf(" [");
if (phdr.p_flags & PF_X)
(void) printf(" execute");
if (phdr.p_flags & PF_R)
(void) printf(" read");
if (phdr.p_flags & PF_W)
(void) printf(" write");
printf(" ]"); NL();
PRINT_FIELD(p_align); NL();
}
(void) elf_end(e);
(void) close(fd);
exit(EXIT_SUCCESS);
}

View File

@ -0,0 +1,92 @@
/*
* Print the names of ELF sections.
*
* $Id: prog4.txt 2133 2011-11-10 08:28:22Z jkoshy $
*/
#include <err.h>
#include <fcntl.h>
#include <gelf.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <vis.h>
int
main(int argc, char **argv)
{
int fd;
Elf *e;
char *name, *p, pc[4*sizeof(char)];
Elf_Scn *scn;
Elf_Data *data;
GElf_Shdr shdr;
size_t n, shstrndx, sz;
if (argc != 2)
errx(EXIT_FAILURE, "usage: %s file-name", argv[0]);
if (elf_version(EV_CURRENT) == EV_NONE)
errx(EXIT_FAILURE, "ELF library initialization "
"failed: %s", elf_errmsg(-1));
if ((fd = open(argv[1], O_RDONLY, 0)) < 0)
err(EXIT_FAILURE, "open \%s\" failed", argv[1]);
if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
errx(EXIT_FAILURE, "elf_begin() failed: %s.",
elf_errmsg(-1));
if (elf_kind(e) != ELF_K_ELF)
errx(EXIT_FAILURE, "%s is not an ELF object.",
argv[1]);
if (elf_getshdrstrndx(e, &shstrndx) != 0) @\co{1}@
errx(EXIT_FAILURE, "elf_getshdrstrndx() failed: %s.",
elf_errmsg(-1));
scn = NULL; @\co{2}@
while ((scn = elf_nextscn(e, scn)) != NULL) { @\co{3}@
if (gelf_getshdr(scn, &shdr) != &shdr) @\co{4}@
errx(EXIT_FAILURE, "getshdr() failed: %s.",
elf_errmsg(-1));
if ((name = elf_strptr(e, shstrndx, shdr.sh_name))
== NULL) @\co{5}@
errx(EXIT_FAILURE, "elf_strptr() failed: %s.",
elf_errmsg(-1));
(void) printf("Section %-4.4jd %s\n", (uintmax_t)
elf_ndxscn(scn), name);
}
if ((scn = elf_getscn(e, shstrndx)) == NULL) @\co{6}@
errx(EXIT_FAILURE, "getscn() failed: %s.",
elf_errmsg(-1));
if (gelf_getshdr(scn, &shdr) != &shdr)
errx(EXIT_FAILURE, "getshdr(shstrndx) failed: %s.",
elf_errmsg(-1));
(void) printf(".shstrab: size=%jd\n", (uintmax_t)
shdr.sh_size);
data = NULL; n = 0;
while (n < shdr.sh_size &&
(data = elf_getdata(scn, data)) != NULL) { @\co{7}@
p = (char *) data->d_buf;
while (p < (char *) data->d_buf + data->d_size) {
if (vis(pc, *p, VIS_WHITE, 0))
printf("%s", pc);
n++; p++;
(void) putchar((n % 16) ? ' ' : '\n');
}
}
(void) putchar('\n');
(void) elf_end(e);
(void) close(fd);
exit(EXIT_SUCCESS);
}

View File

@ -0,0 +1,133 @@
/*
* Create an ELF object.
*
* $Id: prog5.txt 2133 2011-11-10 08:28:22Z jkoshy $
*/
#include <err.h>
#include <fcntl.h>
#include <libelf.h> @\co{1}@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
uint32_t hash_words[] = { @\co{2}@
0x01234567,
0x89abcdef,
0xdeadc0de
};
char string_table[] = { @\co{3}@
/* Offset 0 */ '\0',
/* Offset 1 */ '.', 'f', 'o', 'o', '\0',
/* Offset 6 */ '.', 's', 'h', 's', 't',
'r', 't', 'a', 'b', '\0'
};
int
main(int argc, char **argv)
{
int fd;
Elf *e;
Elf_Scn *scn;
Elf_Data *data;
Elf32_Ehdr *ehdr;
Elf32_Phdr *phdr;
Elf32_Shdr *shdr;
if (argc != 2)
errx(EXIT_FAILURE, "usage: %s file-name", argv[0]);
if (elf_version(EV_CURRENT) == EV_NONE)
errx(EXIT_FAILURE, "ELF library initialization "
"failed: %s", elf_errmsg(-1));
if ((fd = open(argv[1], O_WRONLY|O_CREAT, 0777)) < 0) @\co{4}@
err(EXIT_FAILURE, "open \%s\" failed", argv[1]);
if ((e = elf_begin(fd, ELF_C_WRITE, NULL)) == NULL) @\co{5}@
errx(EXIT_FAILURE, "elf_begin() failed: %s.",
elf_errmsg(-1));
if ((ehdr = elf32_newehdr(e)) == NULL) @\co{6}@
errx(EXIT_FAILURE, "elf32_newehdr() failed: %s.",
elf_errmsg(-1));
ehdr->e_ident[EI_DATA] = ELFDATA2MSB;
ehdr->e_machine = EM_PPC; /* 32-bit PowerPC object */
ehdr->e_type = ET_EXEC;
if ((phdr = elf32_newphdr(e, 1)) == NULL) @\co{7}@
errx(EXIT_FAILURE, "elf32_newphdr() failed: %s.",
elf_errmsg(-1));
if ((scn = elf_newscn(e)) == NULL) @\co{8}@
errx(EXIT_FAILURE, "elf_newscn() failed: %s.",
elf_errmsg(-1));
if ((data = elf_newdata(scn)) == NULL)
errx(EXIT_FAILURE, "elf_newdata() failed: %s.",
elf_errmsg(-1));
data->d_align = 4;
data->d_off = 0LL;
data->d_buf = hash_words;
data->d_type = ELF_T_WORD;
data->d_size = sizeof(hash_words);
data->d_version = EV_CURRENT;
if ((shdr = elf32_getshdr(scn)) == NULL)
errx(EXIT_FAILURE, "elf32_getshdr() failed: %s.",
elf_errmsg(-1));
shdr->sh_name = 1;
shdr->sh_type = SHT_HASH;
shdr->sh_flags = SHF_ALLOC;
shdr->sh_entsize = 0;
if ((scn = elf_newscn(e)) == NULL) @\co{9}@
errx(EXIT_FAILURE, "elf_newscn() failed: %s.",
elf_errmsg(-1));
if ((data = elf_newdata(scn)) == NULL)
errx(EXIT_FAILURE, "elf_newdata() failed: %s.",
elf_errmsg(-1));
data->d_align = 1;
data->d_buf = string_table;
data->d_off = 0LL;
data->d_size = sizeof(string_table);
data->d_type = ELF_T_BYTE;
data->d_version = EV_CURRENT;
if ((shdr = elf32_getshdr(scn)) == NULL)
errx(EXIT_FAILURE, "elf32_getshdr() failed: %s.",
elf_errmsg(-1));
shdr->sh_name = 6;
shdr->sh_type = SHT_STRTAB;
shdr->sh_flags = SHF_STRINGS | SHF_ALLOC;
shdr->sh_entsize = 0;
elf_setshstrndx(e, elf_ndxscn(scn)); @\co{10}@
if (elf_update(e, ELF_C_NULL) < 0) @\co{11}@
errx(EXIT_FAILURE, "elf_update(NULL) failed: %s.",
elf_errmsg(-1));
phdr->p_type = PT_PHDR;
phdr->p_offset = ehdr->e_phoff;
phdr->p_filesz = elf32_fsize(ELF_T_PHDR, 1, EV_CURRENT);
(void) elf_flagphdr(e, ELF_C_SET, ELF_F_DIRTY);
if (elf_update(e, ELF_C_WRITE) < 0) @\co{12}@
errx(EXIT_FAILURE, "elf_update() failed: %s.",
elf_errmsg(-1));
(void) elf_end(e);
(void) close(fd);
exit(EXIT_SUCCESS);
}

View File

@ -0,0 +1,57 @@
/*
* Iterate through an ar(1) archive.
*
* $Id: prog6.txt 2135 2011-11-10 08:59:47Z jkoshy $
*/
#include <err.h>
#include <fcntl.h>
#include <libelf.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main(int argc, char **argv)
{
int fd;
Elf *ar, *e;
Elf_Arhdr *arh;
if (argc != 2)
errx(EXIT_FAILURE, "usage: %s file-name", argv[0]);
if (elf_version(EV_CURRENT) == EV_NONE)
errx(EXIT_FAILURE, "ELF library initialization "
"failed: %s", elf_errmsg(-1));
if ((fd = open(argv[1], O_RDONLY, 0)) < 0)
err(EXIT_FAILURE, "open \%s\" failed", argv[1]);
if ((fd = open(argv[1], O_RDONLY, 0)) < 0) @\co{1}@
err(EXIT_FAILURE, "open \%s\" failed", argv[1]);
if ((ar = elf_begin(fd, ELF_C_READ, NULL)) == NULL) @\co{2}@
errx(EXIT_FAILURE, "elf_begin() failed: %s.",
elf_errmsg(-1));
if (elf_kind(ar) != ELF_K_AR)
errx(EXIT_FAILURE, "%s is not an ar(1) archive.",
argv[1]);
while ((e = elf_begin(fd, ELF_C_READ, ar)) != NULL) { @\co{3}@
if ((arh = elf_getarhdr(e)) == NULL) @\co{4}@
errx(EXIT_FAILURE, "elf_getarhdr() failed: %s.",
elf_errmsg(-1));
(void) printf("%20s %zd\n", arh->ar_name,
arh->ar_size);
(void) elf_next(e); @\co{5}@
(void) elf_end(e); @\co{6}@
}
(void) elf_end(ar);
(void) close(fd);
exit(EXIT_SUCCESS);
}

41
elfcopy/Makefile Normal file
View File

@ -0,0 +1,41 @@
# $Id: Makefile 2290 2011-12-04 07:20:46Z jkoshy $
TOP= ..
PROG= elfcopy
SRCS= archive.c ascii.c binary.c main.c sections.c segments.c symbols.c
WARNS?= 5
DPADD= ${LIBELF} ${LIBELFTC}
LDADD= -lelf -lelftc
.if !defined(LIBELF_AR)
LDADD+= -larchive
.endif
MAN= elfcopy.1 mcs.1 strip.1
NO_SHARED?= yes
LINKS= ${BINDIR}/elfcopy ${BINDIR}/strip \
${BINDIR}/elfcopy ${BINDIR}/mcs
EXTRA_TARGETS= strip mcs
CLEANFILES+= ${EXTRA_TARGETS}
# Create in-place symbolic links to "elfcopy" at build time.
all: ${EXTRA_TARGETS}
${EXTRA_TARGETS}: ${PROG}
ln -s ${PROG} ${.TARGET}
.include "${TOP}/mk/elftoolchain.prog.mk"
.if ${OS_HOST} == "OpenBSD"
CFLAGS+= -I/usr/local/include
LDFLAGS+= -L/usr/local/lib
.endif

528
elfcopy/archive.c Normal file
View File

@ -0,0 +1,528 @@
/*-
* Copyright (c) 2007-2009 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifndef LIBELF_AR
#include <archive.h>
#include <archive_entry.h>
#endif /* ! LIBELF_AR */
#include "elfcopy.h"
ELFTC_VCSID("$Id: archive.c 2370 2011-12-29 12:48:12Z jkoshy $");
#define _ARMAG_LEN 8 /* length of ar magic string */
#define _ARHDR_LEN 60 /* length of ar header */
#define _INIT_AS_CAP 128 /* initial archive string table size */
#define _INIT_SYMOFF_CAP (256*(sizeof(uint32_t))) /* initial so table size */
#define _INIT_SYMNAME_CAP 1024 /* initial sn table size */
#define _MAXNAMELEN_SVR4 15 /* max member name length in svr4 variant */
#ifndef LIBELF_AR
static void ac_read_objs(struct elfcopy *ecp, int ifd);
static void ac_write_cleanup(struct elfcopy *ecp);
static void ac_write_data(struct archive *a, const void *buf, size_t s);
static void ac_write_objs(struct elfcopy *ecp, int ofd);
#endif /* ! LIBELF_AR */
static void add_to_ar_str_table(struct elfcopy *elfcopy, const char *name);
static void add_to_ar_sym_table(struct elfcopy *ecp, const char *name);
static void extract_arsym(struct elfcopy *ecp);
static void process_ar_obj(struct elfcopy *ecp, struct ar_obj *obj);
static void sync_ar(struct elfcopy *ecp);
static void
process_ar_obj(struct elfcopy *ecp, struct ar_obj *obj)
{
struct stat sb;
char *tempfile;
int fd;
/* Output to a temporary file. */
create_tempfile(&tempfile, &fd);
if ((ecp->eout = elf_begin(fd, ELF_C_WRITE, NULL)) == NULL)
errx(EXIT_FAILURE, "elf_begin() failed: %s",
elf_errmsg(-1));
elf_flagelf(ecp->eout, ELF_C_SET, ELF_F_LAYOUT);
create_elf(ecp);
elf_end(ecp->ein);
elf_end(ecp->eout);
free(obj->buf);
obj->buf = NULL;
/* Extract archive symbols. */
if (lseek(fd, 0, SEEK_SET) < 0)
err(EXIT_FAILURE, "lseek failed for '%s'", tempfile);
if ((ecp->eout = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
errx(EXIT_FAILURE, "elf_begin() failed: %s",
elf_errmsg(-1));
extract_arsym(ecp);
elf_end(ecp->eout);
if (fstat(fd, &sb) == -1)
err(EXIT_FAILURE, "fstat %s failed", tempfile);
if (lseek(fd, 0, SEEK_SET) < 0)
err(EXIT_FAILURE, "lseek %s failed", tempfile);
obj->size = sb.st_size;
if ((obj->maddr = malloc(obj->size)) == NULL)
err(EXIT_FAILURE, "memory allocation failed for '%s'",
tempfile);
if ((size_t) read(fd, obj->maddr, obj->size) != obj->size)
err(EXIT_FAILURE, "read failed for '%s'", tempfile);
if (unlink(tempfile))
err(EXIT_FAILURE, "unlink %s failed", tempfile);
free(tempfile);
close(fd);
if (strlen(obj->name) > _MAXNAMELEN_SVR4)
add_to_ar_str_table(ecp, obj->name);
ecp->rela_off += _ARHDR_LEN + obj->size + obj->size % 2;
STAILQ_INSERT_TAIL(&ecp->v_arobj, obj, objs);
}
/*
* Append to the archive string table buffer.
*/
static void
add_to_ar_str_table(struct elfcopy *ecp, const char *name)
{
if (ecp->as == NULL) {
ecp->as_cap = _INIT_AS_CAP;
ecp->as_sz = 0;
if ((ecp->as = malloc(ecp->as_cap)) == NULL)
err(EXIT_FAILURE, "malloc failed");
}
/*
* The space required for holding one member name in as table includes:
* strlen(name) + (1 for '/') + (1 for '\n') + (possibly 1 for padding).
*/
while (ecp->as_sz + strlen(name) + 3 > ecp->as_cap) {
ecp->as_cap *= 2;
ecp->as = realloc(ecp->as, ecp->as_cap);
if (ecp->as == NULL)
err(EXIT_FAILURE, "realloc failed");
}
strncpy(&ecp->as[ecp->as_sz], name, strlen(name));
ecp->as_sz += strlen(name);
ecp->as[ecp->as_sz++] = '/';
ecp->as[ecp->as_sz++] = '\n';
}
/*
* Append to the archive symbol table buffer.
*/
static void
add_to_ar_sym_table(struct elfcopy *ecp, const char *name)
{
if (ecp->s_so == NULL) {
if ((ecp->s_so = malloc(_INIT_SYMOFF_CAP)) == NULL)
err(EXIT_FAILURE, "malloc failed");
ecp->s_so_cap = _INIT_SYMOFF_CAP;
ecp->s_cnt = 0;
}
if (ecp->s_sn == NULL) {
if ((ecp->s_sn = malloc(_INIT_SYMNAME_CAP)) == NULL)
err(EXIT_FAILURE, "malloc failed");
ecp->s_sn_cap = _INIT_SYMNAME_CAP;
ecp->s_sn_sz = 0;
}
if (ecp->s_cnt * sizeof(uint32_t) >= ecp->s_so_cap) {
ecp->s_so_cap *= 2;
ecp->s_so = realloc(ecp->s_so, ecp->s_so_cap);
if (ecp->s_so == NULL)
err(EXIT_FAILURE, "realloc failed");
}
ecp->s_so[ecp->s_cnt] = ecp->rela_off;
ecp->s_cnt++;
/*
* The space required for holding one symbol name in sn table includes:
* strlen(name) + (1 for '\n') + (possibly 1 for padding).
*/
while (ecp->s_sn_sz + strlen(name) + 2 > ecp->s_sn_cap) {
ecp->s_sn_cap *= 2;
ecp->s_sn = realloc(ecp->s_sn, ecp->s_sn_cap);
if (ecp->s_sn == NULL)
err(EXIT_FAILURE, "realloc failed");
}
strncpy(&ecp->s_sn[ecp->s_sn_sz], name, strlen(name));
ecp->s_sn_sz += strlen(name);
ecp->s_sn[ecp->s_sn_sz++] = '\0';
}
static void
sync_ar(struct elfcopy *ecp)
{
size_t s_sz; /* size of archive symbol table. */
size_t pm_sz; /* size of pseudo members */
int i;
/*
* Pad the symbol name string table. It is treated specially because
* symbol name table should be padded by a '\0', not the common '\n'
* for other members. The size of sn table includes the pad bit.
*/
if (ecp->s_cnt != 0 && ecp->s_sn_sz % 2 != 0)
ecp->s_sn[ecp->s_sn_sz++] = '\0';
/*
* Archive string table is padded by a "\n" as the normal members.
* The difference is that the size of archive string table counts
* in the pad bit, while normal members' size fileds do not.
*/
if (ecp->as != NULL && ecp->as_sz % 2 != 0)
ecp->as[ecp->as_sz++] = '\n';
/*
* If there is a symbol table, calculate the size of pseudo members,
* convert previously stored relative offsets to absolute ones, and
* then make them Big Endian.
*
* absolute_offset = htobe32(relative_offset + size_of_pseudo_members)
*/
if (ecp->s_cnt != 0) {
s_sz = (ecp->s_cnt + 1) * sizeof(uint32_t) + ecp->s_sn_sz;
pm_sz = _ARMAG_LEN + (_ARHDR_LEN + s_sz);
if (ecp->as != NULL)
pm_sz += _ARHDR_LEN + ecp->as_sz;
for (i = 0; (size_t)i < ecp->s_cnt; i++)
*(ecp->s_so + i) = htobe32(*(ecp->s_so + i) +
pm_sz);
}
}
/*
* Extract global symbols from archive members.
*/
static void
extract_arsym(struct elfcopy *ecp)
{
Elf_Scn *scn;
GElf_Shdr shdr;
GElf_Sym sym;
Elf_Data *data;
char *name;
size_t n, shstrndx;
int elferr, tabndx, len, i;
if (elf_kind(ecp->eout) != ELF_K_ELF) {
warnx("internal: cannot extract symbols from non-elf object");
return;
}
if (elf_getshstrndx(ecp->eout, &shstrndx) == 0) {
warnx("elf_getshstrndx failed: %s", elf_errmsg(-1));
return;
}
tabndx = -1;
scn = NULL;
while ((scn = elf_nextscn(ecp->eout, scn)) != NULL) {
if (gelf_getshdr(scn, &shdr) != &shdr) {
warnx("elf_getshdr failed: %s", elf_errmsg(-1));
continue;
}
if ((name = elf_strptr(ecp->eout, shstrndx, shdr.sh_name)) ==
NULL) {
warnx("elf_strptr failed: %s", elf_errmsg(-1));
continue;
}
if (strcmp(name, ".strtab") == 0) {
tabndx = elf_ndxscn(scn);
break;
}
}
elferr = elf_errno();
if (elferr != 0)
warnx("elf_nextscn failed: %s", elf_errmsg(elferr));
/* Ignore members without symbol table. */
if (tabndx == -1)
return;
scn = NULL;
while ((scn = elf_nextscn(ecp->eout, scn)) != NULL) {
if (gelf_getshdr(scn, &shdr) != &shdr) {
warnx("elf_getshdr failed: %s", elf_errmsg(-1));
continue;
}
if (shdr.sh_type != SHT_SYMTAB)
continue;
data = NULL;
n = 0;
while (n < shdr.sh_size &&
(data = elf_getdata(scn, data)) != NULL) {
len = data->d_size / shdr.sh_entsize;
for (i = 0; i < len; i++) {
if (gelf_getsym(data, i, &sym) != &sym) {
warnx("gelf_getsym failed: %s",
elf_errmsg(-1));
continue;
}
/* keep only global or weak symbols */
if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL &&
GELF_ST_BIND(sym.st_info) != STB_WEAK)
continue;
/* keep only defined symbols */
if (sym.st_shndx == SHN_UNDEF)
continue;
if ((name = elf_strptr(ecp->eout, tabndx,
sym.st_name)) == NULL) {
warnx("elf_strptr failed: %s",
elf_errmsg(-1));
continue;
}
add_to_ar_sym_table(ecp, name);
}
}
}
elferr = elf_errno();
if (elferr != 0)
warnx("elf_nextscn failed: %s", elf_errmsg(elferr));
}
#ifndef LIBELF_AR
/*
* Convenient wrapper for general libarchive error handling.
*/
#define AC(CALL) do { \
if ((CALL)) \
errx(EXIT_FAILURE, "%s", archive_error_string(a)); \
} while (0)
/* Earlier versions of libarchive had some functions that returned 'void'. */
#if ARCHIVE_VERSION_NUMBER >= 2000000
#define ACV(CALL) AC(CALL)
#else
#define ACV(CALL) do { \
(CALL); \
} while (0)
#endif
int
ac_detect_ar(int ifd)
{
struct archive *a;
struct archive_entry *entry;
int r;
r = -1;
if ((a = archive_read_new()) == NULL)
return (0);
archive_read_support_compression_none(a);
archive_read_support_format_ar(a);
if (archive_read_open_fd(a, ifd, 10240) == ARCHIVE_OK)
r = archive_read_next_header(a, &entry);
archive_read_close(a);
archive_read_finish(a);
return (r == ARCHIVE_OK);
}
void
ac_create_ar(struct elfcopy *ecp, int ifd, int ofd)
{
ac_read_objs(ecp, ifd);
sync_ar(ecp);
ac_write_objs(ecp, ofd);
ac_write_cleanup(ecp);
}
static void
ac_read_objs(struct elfcopy *ecp, int ifd)
{
struct archive *a;
struct archive_entry *entry;
struct ar_obj *obj;
const char *name;
char *buff;
size_t size;
int r;
ecp->rela_off = 0;
if (lseek(ifd, 0, SEEK_SET) == -1)
err(EXIT_FAILURE, "lseek failed");
if ((a = archive_read_new()) == NULL)
errx(EXIT_FAILURE, "%s", archive_error_string(a));
archive_read_support_compression_none(a);
archive_read_support_format_ar(a);
AC(archive_read_open_fd(a, ifd, 10240));
for(;;) {
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_FATAL)
errx(EXIT_FAILURE, "%s", archive_error_string(a));
if (r == ARCHIVE_EOF)
break;
if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY)
warnx("%s", archive_error_string(a));
if (r == ARCHIVE_RETRY)
continue;
name = archive_entry_pathname(entry);
/* skip pseudo members. */
if (strcmp(name, "/") == 0 || strcmp(name, "//") == 0)
continue;
size = archive_entry_size(entry);
if (size > 0) {
if ((buff = malloc(size)) == NULL)
err(EXIT_FAILURE, "malloc failed");
if (archive_read_data(a, buff, size) != (ssize_t)size) {
warnx("%s", archive_error_string(a));
free(buff);
continue;
}
if ((obj = malloc(sizeof(*obj))) == NULL)
err(EXIT_FAILURE, "malloc failed");
if ((obj->name = strdup(name)) == NULL)
err(EXIT_FAILURE, "strdup failed");
obj->buf = buff;
obj->uid = archive_entry_uid(entry);
obj->gid = archive_entry_gid(entry);
obj->md = archive_entry_mode(entry);
obj->mtime = archive_entry_mtime(entry);
if ((ecp->ein = elf_memory(buff, size)) == NULL)
errx(EXIT_FAILURE, "elf_memory() failed: %s",
elf_errmsg(-1));
if (elf_kind(ecp->ein) != ELF_K_ELF)
errx(EXIT_FAILURE,
"file format not recognized");
process_ar_obj(ecp, obj);
}
}
AC(archive_read_close(a));
ACV(archive_read_finish(a));
}
static void
ac_write_objs(struct elfcopy *ecp, int ofd)
{
struct archive *a;
struct archive_entry *entry;
struct ar_obj *obj;
int nr;
if ((a = archive_write_new()) == NULL)
errx(EXIT_FAILURE, "%s", archive_error_string(a));
archive_write_set_format_ar_svr4(a);
archive_write_set_compression_none(a);
AC(archive_write_open_fd(a, ofd));
/* Write the archive symbol table, even if it's empty. */
entry = archive_entry_new();
archive_entry_copy_pathname(entry, "/");
archive_entry_set_mtime(entry, time(NULL), 0);
archive_entry_set_size(entry, (ecp->s_cnt + 1) * sizeof(uint32_t) +
ecp->s_sn_sz);
AC(archive_write_header(a, entry));
nr = htobe32(ecp->s_cnt);
ac_write_data(a, &nr, sizeof(uint32_t));
ac_write_data(a, ecp->s_so, sizeof(uint32_t) * ecp->s_cnt);
ac_write_data(a, ecp->s_sn, ecp->s_sn_sz);
archive_entry_free(entry);
/* Write the archive string table, if exist. */
if (ecp->as != NULL) {
entry = archive_entry_new();
archive_entry_copy_pathname(entry, "//");
archive_entry_set_size(entry, ecp->as_sz);
AC(archive_write_header(a, entry));
ac_write_data(a, ecp->as, ecp->as_sz);
archive_entry_free(entry);
}
/* Write normal members. */
STAILQ_FOREACH(obj, &ecp->v_arobj, objs) {
entry = archive_entry_new();
archive_entry_copy_pathname(entry, obj->name);
archive_entry_set_uid(entry, obj->uid);
archive_entry_set_gid(entry, obj->gid);
archive_entry_set_mode(entry, obj->md);
archive_entry_set_size(entry, obj->size);
archive_entry_set_mtime(entry, obj->mtime, 0);
archive_entry_set_filetype(entry, AE_IFREG);
AC(archive_write_header(a, entry));
ac_write_data(a, obj->maddr, obj->size);
archive_entry_free(entry);
}
AC(archive_write_close(a));
ACV(archive_write_finish(a));
}
static void
ac_write_cleanup(struct elfcopy *ecp)
{
struct ar_obj *obj, *obj_temp;
STAILQ_FOREACH_SAFE(obj, &ecp->v_arobj, objs, obj_temp) {
STAILQ_REMOVE(&ecp->v_arobj, obj, ar_obj, objs);
if (obj->maddr != NULL)
free(obj->maddr);
free(obj->name);
free(obj);
}
free(ecp->as);
free(ecp->s_so);
free(ecp->s_sn);
ecp->as = NULL;
ecp->s_so = NULL;
ecp->s_sn = NULL;
}
/*
* Wrapper for archive_write_data().
*/
static void
ac_write_data(struct archive *a, const void *buf, size_t s)
{
if (archive_write_data(a, buf, s) != (ssize_t)s)
errx(EXIT_FAILURE, "%s", archive_error_string(a));
}
#endif /* ! LIBELF_AR */

1078
elfcopy/ascii.c Normal file

File diff suppressed because it is too large Load Diff

286
elfcopy/binary.c Normal file
View File

@ -0,0 +1,286 @@
/*-
* Copyright (c) 2010,2011 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <err.h>
#include <gelf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "elfcopy.h"
ELFTC_VCSID("$Id: binary.c 2358 2011-12-19 18:22:32Z kaiwang27 $");
/*
* Convert ELF object to `binary'. Sections with SHF_ALLOC flag set
* are copied to the result binary. The relative offsets for each section
* are retained, so the result binary file might contain "holes".
*/
void
create_binary(int ifd, int ofd)
{
Elf *e;
Elf_Scn *scn;
Elf_Data *d;
GElf_Shdr sh;
off_t base, off;
int elferr;
if ((e = elf_begin(ifd, ELF_C_READ, NULL)) == NULL)
errx(EXIT_FAILURE, "elf_begin() failed: %s",
elf_errmsg(-1));
base = 0;
if (lseek(ofd, base, SEEK_SET) < 0)
err(EXIT_FAILURE, "lseek failed");
/*
* Find base offset in the first iteration.
*/
base = -1;
scn = NULL;
while ((scn = elf_nextscn(e, scn)) != NULL) {
if (gelf_getshdr(scn, &sh) == NULL) {
warnx("gelf_getshdr failed: %s", elf_errmsg(-1));
(void) elf_errno();
continue;
}
if ((sh.sh_flags & SHF_ALLOC) == 0 ||
sh.sh_type == SHT_NOBITS ||
sh.sh_size == 0)
continue;
if (base == -1 || (off_t) sh.sh_offset < base)
base = sh.sh_offset;
}
elferr = elf_errno();
if (elferr != 0)
warnx("elf_nextscn failed: %s", elf_errmsg(elferr));
if (base == -1)
return;
/*
* Write out sections in the second iteration.
*/
scn = NULL;
while ((scn = elf_nextscn(e, scn)) != NULL) {
if (gelf_getshdr(scn, &sh) == NULL) {
warnx("gelf_getshdr failed: %s", elf_errmsg(-1));
(void) elf_errno();
continue;
}
if ((sh.sh_flags & SHF_ALLOC) == 0 ||
sh.sh_type == SHT_NOBITS ||
sh.sh_size == 0)
continue;
(void) elf_errno();
if ((d = elf_getdata(scn, NULL)) == NULL) {
elferr = elf_errno();
if (elferr != 0)
warnx("elf_getdata failed: %s", elf_errmsg(-1));
continue;
}
if (d->d_buf == NULL || d->d_size == 0)
continue;
/* lseek to section offset relative to `base'. */
off = sh.sh_offset - base;
if (lseek(ofd, off, SEEK_SET) < 0)
err(EXIT_FAILURE, "lseek failed");
/* Write out section contents. */
if (write(ofd, d->d_buf, d->d_size) != (ssize_t) d->d_size)
err(EXIT_FAILURE, "write failed");
}
elferr = elf_errno();
if (elferr != 0)
warnx("elf_nextscn failed: %s", elf_errmsg(elferr));
}
#define _SYMBOL_NAMSZ 1024
/*
* Convert `binary' to ELF object. The input `binary' is converted to
* a relocatable (.o) file, a few symbols will also be created to make
* it easier to access the binary data in other compilation units.
*/
void
create_elf_from_binary(struct elfcopy *ecp, int ifd, const char *ifn)
{
char name[_SYMBOL_NAMSZ];
struct section *sec, *sec_temp, *shtab;
struct stat sb;
GElf_Ehdr oeh;
GElf_Shdr sh;
void *content;
uint64_t off, data_start, data_end, data_size;
/* Reset internal section list. */
if (!TAILQ_EMPTY(&ecp->v_sec))
TAILQ_FOREACH_SAFE(sec, &ecp->v_sec, sec_list, sec_temp) {
TAILQ_REMOVE(&ecp->v_sec, sec, sec_list);
free(sec);
}
if (fstat(ifd, &sb) == -1)
err(EXIT_FAILURE, "fstat failed");
/* Read the input binary file to a internal buffer. */
if ((content = malloc(sb.st_size)) == NULL)
err(EXIT_FAILURE, "malloc failed");
if (read(ifd, content, sb.st_size) != sb.st_size)
err(EXIT_FAILURE, "read failed");
/*
* TODO: copy the input binary to output binary verbatim if -O is not
* specified.
*/
/* Create EHDR for output .o file. */
if (gelf_newehdr(ecp->eout, ecp->oec) == NULL)
errx(EXIT_FAILURE, "gelf_newehdr failed: %s",
elf_errmsg(-1));
if (gelf_getehdr(ecp->eout, &oeh) == NULL)
errx(EXIT_FAILURE, "gelf_getehdr() failed: %s",
elf_errmsg(-1));
/* Initialise e_ident fields. */
oeh.e_ident[EI_CLASS] = ecp->oec;
oeh.e_ident[EI_DATA] = ecp->oed;
/*
* TODO: Set OSABI according to the OS platform where elfcopy(1)
* was build. (probably)
*/
oeh.e_ident[EI_OSABI] = ELFOSABI_NONE;
oeh.e_machine = ecp->oem;
oeh.e_type = ET_REL;
oeh.e_entry = 0;
ecp->flags |= RELOCATABLE;
/* Create .shstrtab section */
init_shstrtab(ecp);
ecp->shstrtab->off = 0;
/*
* Create `.data' section which contains the binary data. The
* section is inserted immediately after EHDR.
*/
off = gelf_fsize(ecp->eout, ELF_T_EHDR, 1, EV_CURRENT);
if (off == 0)
errx(EXIT_FAILURE, "gelf_fsize() failed: %s", elf_errmsg(-1));
(void) create_external_section(ecp, ".data", NULL, content, sb.st_size,
off, SHT_PROGBITS, ELF_T_BYTE, SHF_ALLOC | SHF_WRITE, 1, 0, 1);
/* Insert .shstrtab after .data section. */
if ((ecp->shstrtab->os = elf_newscn(ecp->eout)) == NULL)
errx(EXIT_FAILURE, "elf_newscn failed: %s",
elf_errmsg(-1));
insert_to_sec_list(ecp, ecp->shstrtab, 1);
/* Insert section header table here. */
shtab = insert_shtab(ecp, 1);
/* Count in .symtab and .strtab section headers. */
shtab->sz += gelf_fsize(ecp->eout, ELF_T_SHDR, 2, EV_CURRENT);
#define _GEN_SYMNAME(S) do { \
snprintf(name, sizeof(name), "%s%s%s", "_binary_", ifn, S); \
} while (0)
/*
* Create symbol table.
*/
create_external_symtab(ecp);
data_start = 0;
data_end = data_start + sb.st_size;
data_size = sb.st_size;
_GEN_SYMNAME("_start");
add_to_symtab(ecp, name, data_start, 0, 1,
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, 1);
_GEN_SYMNAME("_end");
add_to_symtab(ecp, name, data_end, 0, 1,
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, 1);
_GEN_SYMNAME("_size");
add_to_symtab(ecp, name, data_size, 0, SHN_ABS,
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, 1);
finalize_external_symtab(ecp);
create_symtab_data(ecp);
#undef _GEN_SYMNAME
/*
* Write the underlying ehdr. Note that it should be called
* before elf_setshstrndx() since it will overwrite e->e_shstrndx.
*/
if (gelf_update_ehdr(ecp->eout, &oeh) == 0)
errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s",
elf_errmsg(-1));
/* Generate section name string table (.shstrtab). */
ecp->flags |= SYMTAB_EXIST;
set_shstrtab(ecp);
/* Update sh_name pointer for each section header entry. */
update_shdr(ecp, 0);
/* Properly set sh_link field of .symtab section. */
if (gelf_getshdr(ecp->symtab->os, &sh) == NULL)
errx(EXIT_FAILURE, "692 gelf_getshdr() failed: %s",
elf_errmsg(-1));
sh.sh_link = elf_ndxscn(ecp->strtab->os);
if (!gelf_update_shdr(ecp->symtab->os, &sh))
errx(EXIT_FAILURE, "gelf_update_shdr() failed: %s",
elf_errmsg(-1));
/* Renew oeh to get the updated e_shstrndx. */
if (gelf_getehdr(ecp->eout, &oeh) == NULL)
errx(EXIT_FAILURE, "gelf_getehdr() failed: %s",
elf_errmsg(-1));
/* Resync section offsets. */
resync_sections(ecp);
/* Store SHDR offset in EHDR. */
oeh.e_shoff = shtab->off;
/* Update ehdr since we modified e_shoff. */
if (gelf_update_ehdr(ecp->eout, &oeh) == 0)
errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s",
elf_errmsg(-1));
/* Write out the output elf object. */
if (elf_update(ecp->eout, ELF_C_WRITE) < 0)
errx(EXIT_FAILURE, "elf_update() failed: %s",
elf_errmsg(-1));
/* Release allocated resource. */
free(content);
free_elf(ecp);
}

323
elfcopy/elfcopy.1 Normal file
View File

@ -0,0 +1,323 @@
.\" Copyright (c) 2008-2009,2011 Joseph Koshy. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" This software is provided by Joseph Koshy ``as is'' and
.\" any express or implied warranties, including, but not limited to, the
.\" implied warranties of merchantability and fitness for a particular purpose
.\" are disclaimed. in no event shall Joseph Koshy be liable
.\" for any direct, indirect, incidental, special, exemplary, or consequential
.\" damages (including, but not limited to, procurement of substitute goods
.\" or services; loss of use, data, or profits; or business interruption)
.\" however caused and on any theory of liability, whether in contract, strict
.\" liability, or tort (including negligence or otherwise) arising in any way
.\" out of the use of this software, even if advised of the possibility of
.\" such damage.
.\"
.\" $Id: elfcopy.1 2373 2011-12-30 07:13:44Z jkoshy $
.\"
.Dd October 03, 2011
.Os
.Dt ELFCOPY 1
.Sh NAME
.Nm elfcopy
.Nd copy and translate object files
.Sh SYNOPSIS
.Nm
.Op Fl I Ar objformat | Fl s Ar objformat | Fl -input-target= Ns Ar objformat
.Op Fl K Ar symbolname | Fl -keep-symbol= Ns Ar symbolname
.Op Fl L Ar symbolname | Fl -localize-symbol= Ns Ar symbolname
.Op Fl N Ar symbolname | Fl -strip-symbol= Ns Ar symbolname
.Op Fl O Ar objformat | Fl -output-target= Ns Ar objformat
.Op Fl R Ar sectionname | Fl -remove-section= Ns Ar sectionname
.Op Fl S | Fl -strip-all
.Op Fl V | Fl -version
.Op Fl W Ar symbolname | Fl -weaken-symbol= Ns Ar symbolname
.Op Fl X | Fl -discard-locals
.Op Fl d | Fl g | Fl -strip-debug
.Op Fl h | Fl -help
.Op Fl j Ar sectionname | Fl -only-section= Ns Ar sectionname
.Op Fl p | Fl -preserve-dates
.Op Fl w | Fl -wildcard
.Op Fl x | Fl -discard-all
.Op Fl -add-section Ar sectionname Ns = Ns Ar filename
.Oo
.Fl -adjust-section-vma Ar section Ns {+|-|=} Ns Ar val |
.Fl -change-section-address Ar section Ns {+|-|=} Ns Ar val
.Oc
.Oo
.Fl -adjust-start Ns = Ns Ar increment |
.Fl -change-start Ns = Ns Ar increment
.Oc
.Oo
.Fl -adjust-vma Ns = Ns Ar increment |
.Fl -change-addresses Ns = Ns Ar increment
.Oc
.Op Fl -adjust-warnings | Fl -change-warnings
.Op Fl -change-section-lma Ar section Ns {+|-|=} Ns Ar val
.Op Fl -change-section-vma Ar section Ns {+|-|=} Ns Ar val
.Op Fl -gap-fill Ns = Ns Ar val
.Op Fl -no-adjust-warnings | Fl -no-change-warnings
.Op Fl -only-keep-debug
.Op Fl -pad-to Ns = Ns Ar address
.Op Fl -prefix-alloc-sections Ns = Ns Ar string
.Op Fl -prefix-sections Ns = Ns Ar string
.Op Fl -prefix-symbols Ns = Ns Ar string
.Op Fl -rename-section Ar oldname Ns = Ns Ar newname Ns Op Ar ,flags
.Op Fl -set-section-flags Ar sectionname Ns = Ns Ar flags
.Op Fl -set-start Ns = Ns Ar address
.Op Fl -srec-forceS3
.Op Fl -srec-len Ns = Ns Ar val
.Op Fl -strip-unneeded
.Ar infile
.Op Ar outfile
.Sh DESCRIPTION
The
.Nm
utility copies the content of the ELF object named by argument
.Ar infile
to that named by argument
.Ar outfile ,
transforming it according to the command line options specified.
If argument
.Ar outfile
is not specified,
.Nm
will create a temporary file and will subsequently rename it as
.Ar infile .
.Pp
The
.Nm
utility supports the following options:
.Bl -tag -width indent
.It Fl I Ar objformat | Fl s Ar objformat | Fl -input-target= Ns Ar objformat
Specify that the input file named by the argument
.Ar infile
is in the object format specified by the argument
.Ar objformat .
.It Fl K Ar symbolname | Fl -keep-symbol= Ns Ar symbolname
Copy the symbol named by argument
.Ar symbolname
to the output.
.It Fl L Ar symbolname | Fl -localize-symbol= Ns Ar symbolname
Make the symbol named by argument
.Ar symbolname
local to the output file.
.It Fl N Ar symbol | Fl -strip-symbol= Ns Ar symbolname
Do not copy the symbol named by argument
.Ar symbolname
to the output.
.It Fl O Ar objformat | Fl -output-target= Ns Ar objformat
Write the output file using the object format specified in argument
.Ar objformat .
.It Fl R Ar sectionname | Fl -remove-section= Ns Ar sectionname
Remove any section with name
.Ar sectionname
from the output file.
.It Fl S | Fl -strip-all
Do not copy symbol and relocation information to the target file.
.It Fl V | Fl -version
Print a version identifier and exit.
.It Fl W Ar symbolname | Fl -weaken-symbol= Ns Ar symbolname
Mark the symbol named by argument
.Ar symbolname
as weak in the output.
.It Fl X | Fl -discard-locals
Do not copy compiler generated local symbols to the output.
.It Fl d | Fl g | Fl -strip-debug
Do not copy debugging information to the target file.
.It Fl h | Fl -help
Display a help message and exit.
.It Fl j Ar sectionname | Fl -only-section= Ns Ar sectionname
Copy only the section named by argument
.Ar sectionname
to the output.
.It Fl p | Fl -preserve-dates
Set the access and modification times of the output file to the
same as those of the input.
.It Fl w | Fl -wildcard
Use shell-style patterns to name symbols.
The following meta-characters are recognized in patterns:
.Bl -tag -width "...." -compact
.It Li !
If this is the first character of the pattern, invert the sense of the
pattern match.
.It Li *
Matches any string of characters in a symbol name.
.It Li ?
Matches zero or one character in a symbol name.
.It Li [
Mark the start of a character class.
.It Li \e
Remove the special meaning of the next character in the pattern.
.It Li ]
Mark the end of a character class.
.El
.It Fl x | Fl -discard-all
Do not copy non-global symbols to the output.
.It Fl -add-section Ar sectionname Ns = Ns Ar filename
Add a new section to the output file with name
.Ar sectionname .
The contents of the section are taken from the file named by
argument
.Ar filename .
The size of the section will be the number of bytes in file
.Ar filename .
.It Xo
.Fl -adjust-section-vma Ar section Ns {+|-|=} Ns Ar val |
.Fl -change-section-address Ar section Ns {+|-|=} Ns Ar val
.Xc
Depending on the operator specified, increase, decrease or set both
the virtual memory address and the load memory address of the section
named by the argument
.Ar section .
The argument
.Ar val
specifies the desired increment, decrement or new value for the
address.
.It Xo
.Fl -adjust-start Ns = Ns Ar increment |
.Fl -change-start Ns = Ns Ar increment
.Xc
Increase the entry point address of the output ELF object by the value
specified in the argument
.Ar increment .
.It Xo
.Fl -adjust-vma Ns = Ns Ar increment |
.Fl -change-addresses Ns = Ns Ar increment
.Xc
Increase the virtual memory address and the load memory address of all
sections by the value specified by the argument
.Ar increment .
.It Fl -adjust-warnings | Fl -change-warnings
Issue a warning if the section specified by the options
.Fl -change-section-address ,
.Fl -change-section-lma
or
.Fl -change-section-vma
does not exist in the input object.
This is the default.
.It Fl -change-section-lma Ar section Ns {+|-|=} Ns Ar val
Change or set the load memory address of the section named by the
argument
.Ar section .
Depending on the operator specified, the value in argument
.Ar val
will be used as an increment, a decrement or as the new value
of the load memory address.
.It Fl -change-section-vma Ar section Ns {+|-|=} Ns Ar val
Change or set the virtual memory address of the section named by the
argument
.Ar section .
Depending on the operator specified, the value in argument
.Ar val
will be used as an increment, a decrement or as the new value
of the virtual memory address.
.It Fl -gap-fill Ns = Ns Ar val
Fill the gaps between sections with the byte value specified by
the argument
.Ar val .
.It Fl -no-adjust-warnings | Fl -no-change-warnings
Do not issue a warning if the section specified by the options
.Fl -change-section-address ,
.Fl -change-section-lma
or
.Fl -change-section-vma
is missing in the input object.
.It Fl -only-keep-debug
Copy only debugging information to the output file.
.It Fl -pad-to Ns = Ns Ar address
Pad the load memory address of the output object to the value
specified by the argument
.Ar address
by increasing the size of the section with the highest load memory
address.
.It Fl -prefix-alloc-sections Ns = Ns Ar string
Prefix the section names of all the allocated sections with
.Ar string .
.It Fl -prefix-sections Ns = Ns Ar string
Prefix the section names of all the sections with
.Ar string .
.It Fl -prefix-symbols Ns = Ns Ar string
Prefix the symbol names of all the symbols with
.Ar string .
.It Fl -rename-section Ar oldname Ns = Ns Ar newname Ns Op Ar ,flags
Rename the section named by argument
.Ar oldname
to
.Ar newname ,
optionally changing the sections flags to that specified by argument
.Ar flags .
Allowed values for the argument
.Ar flags
are as for option
.Fl -set-section-flags
below.
.It Fl -set-section-flags Ar sectionname Ns = Ns Ar flags
Set the flags for the section named by argument
.Ar sectionname
to those specified by argument
.Ar flags .
Argument
.Ar flags
is a comma separated list of the following flag names:
.Bl -tag -width "readonly" -compact
.It alloc
The section occupies space in the output file.
.It code
The section contains machine instructions.
.It contents
This flag is accepted but is ignored.
.It data
The section contains writeable data.
.It debug
The section holds debugging information.
.It load
The section is loadable.
.It noload
The section should not be loaded into memory.
.It readonly
The section is not writable.
.It rom
The section contains ROM'able contents.
.It share
This flag is accepted but is ignored.
.El
.It Fl -set-start Ns = Ns Ar address
Set the start address of the output ELF object to the value specified
by the argument
.Ar address .
.It Fl -srec-forceS3
Only generate S-records of type
.Dq S3 .
This option is only meaningful when the output target is set to
.Dq srec .
.It Fl -srec-len Ns = Ns Ar val
Set the maximum length of an S-record line to
.Ar val .
This option is only meaningful when the output target is set to
.Dq srec .
.It Fl -strip-unneeded
Do not copy symbols that are not needed for relocation processing.
.El
.Sh DIAGNOSTICS
.Ex -std
.Sh SEE ALSO
.Xr ar 1 ,
.Xr ld 1 ,
.Xr mcs 1 ,
.Xr strip 1 ,
.Xr elf 3 ,
.Xr ar 5 ,
.Xr elf 5
.Sh HISTORY
.Nm
has been implemented by
.An "Kai Wang" Aq kaiwang27@users.sourceforge.net .

313
elfcopy/elfcopy.h Normal file
View File

@ -0,0 +1,313 @@
/*-
* Copyright (c) 2007-2013 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: elfcopy.h 2970 2013-12-01 15:22:12Z kaiwang27 $
*/
#include <sys/queue.h>
#include <gelf.h>
#include <libelftc.h>
#include "_elftc.h"
/*
* User specified symbol operation (strip, keep, localize, globalize,
* weaken, rename, etc).
*/
struct symop {
const char *name;
const char *newname;
#define SYMOP_KEEP 0x0001U
#define SYMOP_STRIP 0x0002U
#define SYMOP_GLOBALIZE 0x0004U
#define SYMOP_LOCALIZE 0x0008U
#define SYMOP_KEEPG 0x0010U
#define SYMOP_WEAKEN 0x0020U
#define SYMOP_REDEF 0x0040U
unsigned int op;
STAILQ_ENTRY(symop) symop_list;
};
/* File containing symbol list. */
struct symfile {
dev_t dev;
ino_t ino;
size_t size;
char *data;
unsigned int op;
STAILQ_ENTRY(symfile) symfile_list;
};
/* Sections to copy/remove/rename/... */
struct sec_action {
const char *name;
const char *addopt;
const char *newname;
const char *string;
uint64_t lma;
uint64_t vma;
int64_t lma_adjust;
int64_t vma_adjust;
#define SF_ALLOC 0x0001U
#define SF_LOAD 0x0002U
#define SF_NOLOAD 0x0004U
#define SF_READONLY 0x0008U
#define SF_DEBUG 0x0010U
#define SF_CODE 0x0020U
#define SF_DATA 0x0040U
#define SF_ROM 0x0080U
#define SF_SHARED 0X0100U
#define SF_CONTENTS 0x0200U
int flags;
int add;
int append;
int compress;
int copy;
int print;
int remove;
int rename;
int setflags;
int setlma;
int setvma;
STAILQ_ENTRY(sec_action) sac_list;
};
/* Sections to add from file. */
struct sec_add {
char *name;
char *content;
size_t size;
STAILQ_ENTRY(sec_add) sadd_list;
};
struct segment;
/* Internal data structure for sections. */
struct section {
struct segment *seg; /* containing segment */
const char *name; /* section name */
char *newname; /* new section name */
Elf_Scn *is; /* input scn */
Elf_Scn *os; /* output scn */
void *buf; /* section content */
uint8_t *pad; /* section padding */
uint64_t off; /* section offset */
uint64_t sz; /* section size */
uint64_t cap; /* section capacity */
uint64_t align; /* section alignment */
uint64_t type; /* section type */
uint64_t vma; /* section virtual addr */
uint64_t lma; /* section load addr */
uint64_t pad_sz;/* section padding size */
int loadable; /* whether loadable */
int pseudo;
int nocopy;
TAILQ_ENTRY(section) sec_list; /* next section */
};
/* Internal data structure for segments. */
struct segment {
uint64_t addr; /* load addr */
uint64_t off; /* file offset */
uint64_t fsz; /* file size */
uint64_t msz; /* memory size */
uint64_t type; /* segment type */
int remove; /* whether remove */
int nsec; /* number of sections contained */
struct section **v_sec; /* list of sections contained */
STAILQ_ENTRY(segment) seg_list; /* next segment */
};
/*
* In-memory representation of ar(1) archive member(object).
*/
struct ar_obj {
char *name; /* member name */
char *buf; /* member content */
void *maddr; /* mmap start address */
uid_t uid; /* user id */
gid_t gid; /* group id */
mode_t md; /* octal file permissions */
size_t size; /* member size */
time_t mtime; /* modification time */
STAILQ_ENTRY(ar_obj) objs;
};
/*
* Structure encapsulates the "global" data for "elfcopy" program.
*/
struct elfcopy {
const char *progname; /* program name */
int iec; /* elfclass of input object */
Elftc_Bfd_Target_Flavor itf; /* flavour of input object */
Elftc_Bfd_Target_Flavor otf; /* flavour of output object */
const char *otgt; /* output target name */
int oec; /* elfclass of output object */
unsigned char oed; /* endianess of output object */
int oem; /* EM_XXX of output object */
int abi; /* OSABI of output object */
Elf *ein; /* ELF descriptor of input object */
Elf *eout; /* ELF descriptor of output object */
int iphnum; /* num. of input object phdr entries */
int ophnum; /* num. of output object phdr entries */
int nos; /* num. of output object sections */
enum {
STRIP_NONE = 0,
STRIP_ALL,
STRIP_DEBUG,
STRIP_NONDEBUG,
STRIP_UNNEEDED
} strip;
#define EXECUTABLE 0x00000001U
#define DYNAMIC 0x00000002U
#define RELOCATABLE 0x00000004U
#define SYMTAB_EXIST 0x00000010U
#define SYMTAB_INTACT 0x00000020U
#define KEEP_GLOBAL 0x00000040U
#define DISCARD_LOCAL 0x00000080U
#define WEAKEN_ALL 0x00000100U
#define PRESERVE_DATE 0x00001000U
#define SREC_FORCE_S3 0x00002000U
#define SREC_FORCE_LEN 0x00004000U
#define SET_START 0x00008000U
#define GAP_FILL 0x00010000U
#define WILDCARD 0x00020000U
#define NO_CHANGE_WARN 0x00040000U
#define SEC_ADD 0x00080000U
#define SEC_APPEND 0x00100000U
#define SEC_COMPRESS 0x00200000U
#define SEC_PRINT 0x00400000U
#define SEC_REMOVE 0x00800000U
#define SEC_COPY 0x01000000U
#define DISCARD_LLABEL 0x02000000U
int flags; /* elfcopy run control flags. */
int64_t change_addr; /* Section address adjustment. */
int64_t change_start; /* Entry point adjustment. */
uint64_t set_start; /* Entry point value. */
unsigned long srec_len; /* S-Record length. */
uint64_t pad_to; /* load address padding. */
uint8_t fill; /* gap fill value. */
char *prefix_sec; /* section prefix. */
char *prefix_alloc; /* alloc section prefix. */
char *prefix_sym; /* symbol prefix. */
char *debuglink; /* GNU debuglink file. */
struct section *symtab; /* .symtab section. */
struct section *strtab; /* .strtab section. */
struct section *shstrtab; /* .shstrtab section. */
uint64_t *secndx; /* section index map. */
uint64_t *symndx; /* symbol index map. */
unsigned char *v_rel; /* symbols needed by relocation. */
unsigned char *v_secsym; /* sections with section symbol. */
STAILQ_HEAD(, segment) v_seg; /* list of segments. */
STAILQ_HEAD(, sec_action) v_sac;/* list of section operations. */
STAILQ_HEAD(, sec_add) v_sadd; /* list of sections to add. */
STAILQ_HEAD(, symop) v_symop; /* list of symbols operations. */
STAILQ_HEAD(, symfile) v_symfile; /* list of symlist files. */
TAILQ_HEAD(, section) v_sec; /* list of sections. */
/*
* Fields for the ar(1) archive.
*/
char *as; /* buffer for archive string table. */
size_t as_sz; /* current size of as table. */
size_t as_cap; /* capacity of as table buffer. */
uint32_t s_cnt; /* current number of symbols. */
uint32_t *s_so; /* symbol offset table. */
size_t s_so_cap; /* capacity of so table buffer. */
char *s_sn; /* symbol name table */
size_t s_sn_cap; /* capacity of sn table buffer. */
size_t s_sn_sz; /* current size of sn table. */
off_t rela_off; /* offset relative to pseudo members. */
STAILQ_HEAD(, ar_obj) v_arobj; /* archive object(member) list. */
};
void add_section(struct elfcopy *_ecp, const char *_optarg);
void add_to_shstrtab(struct elfcopy *_ecp, const char *_name);
void add_to_symop_list(struct elfcopy *_ecp, const char *_name,
const char *_newname, unsigned int _op);
void add_to_symtab(struct elfcopy *_ecp, const char *_name,
uint64_t _st_value, uint64_t _st_size, uint16_t _st_shndx,
unsigned char _st_info, unsigned char _st_other, int _ndx_known);
int add_to_inseg_list(struct elfcopy *_ecp, struct section *_sec);
void adjust_addr(struct elfcopy *_ecp);
void copy_content(struct elfcopy *_ecp);
void copy_data(struct section *_s);
void copy_phdr(struct elfcopy *_ecp);
void copy_shdr(struct elfcopy *_ecp, struct section *_s, const char *_name,
int _copy, int _sec_flags);
void create_binary(int _ifd, int _ofd);
void create_elf(struct elfcopy *_ecp);
void create_elf_from_binary(struct elfcopy *_ecp, int _ifd, const char *ifn);
void create_elf_from_ihex(struct elfcopy *_ecp, int _ifd);
void create_elf_from_srec(struct elfcopy *_ecp, int _ifd);
struct section *create_external_section(struct elfcopy *_ecp, const char *_name,
char *_newname, void *_buf, uint64_t _size, uint64_t _off, uint64_t _stype,
Elf_Type _dtype, uint64_t flags, uint64_t _align, uint64_t _vma,
int _loadable);
void create_external_symtab(struct elfcopy *_ecp);
void create_ihex(int _ifd, int _ofd);
void create_scn(struct elfcopy *_ecp);
void create_srec(struct elfcopy *_ecp, int _ifd, int _ofd, const char *_ofn);
void create_symtab(struct elfcopy *_ecp);
void create_symtab_data(struct elfcopy *_ecp);
void create_tempfile(char **_fn, int *_fd);
void finalize_external_symtab(struct elfcopy *_ecp);
void free_elf(struct elfcopy *_ecp);
void free_sec_act(struct elfcopy *_ecp);
void free_sec_add(struct elfcopy *_ecp);
void free_symtab(struct elfcopy *_ecp);
void init_shstrtab(struct elfcopy *_ecp);
void insert_to_sec_list(struct elfcopy *_ecp, struct section *_sec,
int _tail);
struct section *insert_shtab(struct elfcopy *_ecp, int tail);
int is_remove_reloc_sec(struct elfcopy *_ecp, uint32_t _sh_info);
int is_remove_section(struct elfcopy *_ecp, const char *_name);
struct sec_action *lookup_sec_act(struct elfcopy *_ecp,
const char *_name, int _add);
struct symop *lookup_symop_list(struct elfcopy *_ecp, const char *_name,
unsigned int _op);
void resync_sections(struct elfcopy *_ecp);
void set_shstrtab(struct elfcopy *_ecp);
void setup_phdr(struct elfcopy *_ecp);
void update_shdr(struct elfcopy *_ecp, int _update_link);
#ifndef LIBELF_AR
int ac_detect_ar(int _ifd);
void ac_create_ar(struct elfcopy *_ecp, int _ifd, int _ofd);
#endif /* ! LIBELF_AR */

1500
elfcopy/main.c Normal file

File diff suppressed because it is too large Load Diff

125
elfcopy/mcs.1 Normal file
View File

@ -0,0 +1,125 @@
.\" Copyright (c) 2011 Joseph Koshy. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY JOSEPH KOSHY ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL JOSEPH KOSHY BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $Id: mcs.1 2247 2011-11-29 08:41:34Z jkoshy $
.\"
.Dd November 29, 2011
.Os
.Dt MCS 1
.Sh NAME
.Nm mcs
.Nd manipulate the comment section of an ELF object
.Sh SYNOPSIS
.Nm
.Op Fl a Ar string
.Op Fl c
.Op Fl n Ar name
.Op Fl p
.Ar
.Nm
.Fl d
.Op Fl n Ar name
.Ar
.Nm
.Fl h | Fl -help
.Nm
.Fl V | Fl -version
.Sh DESCRIPTION
The
.Nm
utility is used to manipulate comment sections in an ELF object.
If a command-line argument
.Ar file
names an
.Xr ar 1
archive, then
.Nm
will operate on the ELF objects contained in the archive.
.Pp
By default
.Nm
operates on the ELF section named
.Dq .comment .
This may be changed using the
.Fl n
option.
.Pp
The
.Nm
utility supports the following options:
.Bl -tag -width ".Fl a Ar string"
.It Fl a Ar string
Append the text in
.Ar string
to the comment section.
This option may be specified multiple times.
.It Fl c
Compress the comment section by removing duplicate entries.
.It Fl d
Delete the comment section from the ELF object.
.It Fl h | Fl -help
Display a usage message and exit.
.It Fl n Ar name
Operate on the section named
.Ar name .
.It Fl p
Print the contents of the comment section.
This step is taken after actions specified by the
.Fl a
and
.Fl c
options (if any) are completed.
.It Fl V | Fl -version
Print a version identifier and exit.
.El
.Sh COMPATIBILITY
The behavior of the
.Nm
utility differs from its SVR4 counterpart in the following ways:
.Bl -bullet -compact
.It
If the
.Fl d
option is specified, it causes any
.Fl a ,
.Fl c
and
.Fl p
options present to be ignored.
.It
The order of options
.Fl a ,
.Fl c ,
.Fl d ,
and
.Fl p
on the command line is not significant.
.El
.Sh DIAGNOSTICS
.Ex -std
.Sh SEE ALSO
.Xr ar 1 ,
.Xr elfcopy 1 ,
.Xr ld 1 ,
.Xr nm 1 ,
.Xr strip 1

1518
elfcopy/sections.c Normal file

File diff suppressed because it is too large Load Diff

493
elfcopy/segments.c Normal file
View File

@ -0,0 +1,493 @@
/*-
* Copyright (c) 2007-2010,2012 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/queue.h>
#include <err.h>
#include <gelf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "elfcopy.h"
ELFTC_VCSID("$Id: segments.c 2542 2012-08-12 16:14:15Z kaiwang27 $");
static void insert_to_inseg_list(struct segment *seg, struct section *sec);
/*
* elfcopy's segment handling is relatively simpler and less powerful than
* libbfd. Program headers are modified or copied from input to output objects,
* but never re-generated. As a result, if the input object has incorrect
* program headers, the output object's program headers will remain incorrect
* or become even worse.
*/
/*
* Check whether a section is "loadable". If so, add it to the
* corresponding segment list(s) and return 1.
*/
int
add_to_inseg_list(struct elfcopy *ecp, struct section *s)
{
struct segment *seg;
int loadable;
if (ecp->ophnum == 0)
return (0);
/*
* Segment is a different view of an ELF object. One segment can
* contain one or more sections, and one section can be included
* in one or more segments, or not included in any segment at all.
* We call those sections which can be found in one or more segments
* "loadable" sections, and call the rest "unloadable" sections.
* We keep track of "loadable" sections in their containing
* segment(s)' v_sec queue. These information are later used to
* recalculate the extents of segments, when sections are removed,
* for example.
*/
loadable = 0;
STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
if (s->off < seg->off)
continue;
if (s->off + s->sz > seg->off + seg->fsz &&
s->type != SHT_NOBITS)
continue;
if (s->off + s->sz > seg->off + seg->msz)
continue;
insert_to_inseg_list(seg, s);
if (seg->type == PT_LOAD)
s->seg = seg;
s->lma = seg->addr + (s->off - seg->off);
loadable = 1;
}
return (loadable);
}
void
adjust_addr(struct elfcopy *ecp)
{
struct section *s, *s0;
struct segment *seg;
struct sec_action *sac;
uint64_t dl, lma, old_vma, start, end;
int found, i;
/*
* Apply VMA and global LMA changes in the first iteration.
*/
TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {
/* Only adjust loadable section's address. */
if (!s->loadable || s->seg == NULL)
continue;
/* Apply global LMA adjustment. */
if (ecp->change_addr != 0)
s->lma += ecp->change_addr;
if (!s->pseudo) {
old_vma = s->vma;
/* Apply global VMA adjustment. */
if (ecp->change_addr != 0)
s->vma += ecp->change_addr;
/* Apply section VMA adjustment. */
sac = lookup_sec_act(ecp, s->name, 0);
if (sac == NULL)
continue;
if (sac->setvma)
s->vma = sac->vma;
if (sac->vma_adjust != 0)
s->vma += sac->vma_adjust;
}
}
/*
* Apply sections LMA change in the second iteration.
*/
TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {
/* Only adjust loadable section's LMA. */
if (!s->loadable || s->seg == NULL)
continue;
/*
* Check if there is a LMA change request for this
* section.
*/
sac = lookup_sec_act(ecp, s->name, 0);
if (sac == NULL)
continue;
if (!sac->setlma && sac->lma_adjust == 0)
continue;
lma = s->lma;
if (sac->setlma)
lma = sac->lma;
if (sac->lma_adjust != 0)
lma += sac->lma_adjust;
if (lma == s->lma)
continue;
/*
* Check if the LMA change is viable.
*
* 1. Check if the new LMA is properly aligned accroding to
* section alignment.
*
* 2. Compute the new extent of segment that contains this
* section, make sure it doesn't overlap with other
* segments.
*/
#ifdef DEBUG
printf("LMA for section %s: %#jx\n", s->name, lma);
#endif
if (lma % s->align != 0)
errx(EXIT_FAILURE, "The load address %#jx for "
"section %s is not aligned to %ju",
(uintmax_t) lma, s->name, s->align);
if (lma < s->lma) {
/* Move section to lower address. */
if (lma < s->lma - s->seg->addr)
errx(EXIT_FAILURE, "Not enough space to move "
"section %s load address to %#jx", s->name,
(uintmax_t) lma);
start = lma - (s->lma - s->seg->addr);
if (s == s->seg->v_sec[s->seg->nsec - 1])
end = start + s->seg->msz;
else
end = s->seg->addr + s->seg->msz;
} else {
/* Move section to upper address. */
if (s == s->seg->v_sec[0])
start = lma;
else
start = s->seg->addr;
end = lma + (s->seg->addr + s->seg->msz - s->lma);
if (end < start)
errx(EXIT_FAILURE, "Not enough space to move "
"section %s load address to %#jx", s->name,
(uintmax_t) lma);
}
#ifdef DEBUG
printf("new extent for segment containing %s: (%#jx,%#jx)\n",
s->name, start, end);
#endif
STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
if (seg == s->seg || seg->type != PT_LOAD)
continue;
if (start > seg->addr + seg->msz)
continue;
if (end < seg->addr)
continue;
errx(EXIT_FAILURE, "The extent of segment containing "
"section %s overlaps with segment(%#jx,%#jx)",
s->name, seg->addr, seg->addr + seg->msz);
}
/*
* Update section LMA and file offset.
*/
if (lma < s->lma) {
/*
* To move a section to lower load address, we decrease
* the load addresses of the section and all the
* sections that are before it, and we increase the
* file offsets of all the sections that are after it.
*/
dl = s->lma - lma;
for (i = 0; i < s->seg->nsec; i++) {
s0 = s->seg->v_sec[i];
s0->lma -= dl;
#ifdef DEBUG
printf("section %s LMA set to %#jx\n",
s0->name, (uintmax_t) s0->lma);
#endif
if (s0 == s)
break;
}
for (i = i + 1; i < s->seg->nsec; i++) {
s0 = s->seg->v_sec[i];
s0->off += dl;
#ifdef DEBUG
printf("section %s offset set to %#jx\n",
s0->name, (uintmax_t) s0->off);
#endif
}
} else {
/*
* To move a section to upper load address, we increase
* the load addresses of the section and all the
* sections that are after it, and we increase the
* their file offsets too unless the section in question
* is the first in its containing segment.
*/
dl = lma - s->lma;
for (i = 0; i < s->seg->nsec; i++)
if (s->seg->v_sec[i] == s)
break;
if (i >= s->seg->nsec)
errx(EXIT_FAILURE, "Internal: section `%s' not"
" found in its containing segement",
s->name);
for (; i < s->seg->nsec; i++) {
s0 = s->seg->v_sec[i];
s0->lma += dl;
#ifdef DEBUG
printf("section %s LMA set to %#jx\n",
s0->name, (uintmax_t) s0->lma);
#endif
if (s != s->seg->v_sec[0]) {
s0->off += dl;
#ifdef DEBUG
printf("section %s offset set to %#jx\n",
s0->name, (uintmax_t) s0->off);
#endif
}
}
}
}
/*
* Apply load address padding.
*/
if (ecp->pad_to != 0) {
/*
* Find the section with highest load address.
*/
s = NULL;
STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
if (seg->type != PT_LOAD)
continue;
for (i = seg->nsec - 1; i >= 0; i--)
if (seg->v_sec[i]->type != SHT_NOBITS)
break;
if (i < 0)
continue;
if (s == NULL)
s = seg->v_sec[i];
else {
s0 = seg->v_sec[i];
if (s0->lma > s->lma)
s = s0;
}
}
if (s == NULL)
goto issue_warn;
/* No need to pad if the pad_to address is lower. */
if (ecp->pad_to <= s->lma + s->sz)
goto issue_warn;
s->pad_sz = ecp->pad_to - (s->lma + s->sz);
#ifdef DEBUG
printf("pad section %s load to address %#jx by %#jx\n", s->name,
(uintmax_t) ecp->pad_to, (uintmax_t) s->pad_sz);
#endif
}
issue_warn:
/*
* Issue a warning if there are VMA/LMA adjust requests for
* some nonexistent sections.
*/
if ((ecp->flags & NO_CHANGE_WARN) == 0) {
STAILQ_FOREACH(sac, &ecp->v_sac, sac_list) {
if (!sac->setvma && !sac->setlma &&
!sac->vma_adjust && !sac->lma_adjust)
continue;
found = 0;
TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {
if (s->pseudo || s->name == NULL)
continue;
if (!strcmp(s->name, sac->name)) {
found = 1;
break;
}
}
if (!found)
warnx("cannot find section `%s'", sac->name);
}
}
}
static void
insert_to_inseg_list(struct segment *seg, struct section *sec)
{
struct section *s;
int i;
seg->nsec++;
seg->v_sec = realloc(seg->v_sec, seg->nsec * sizeof(*seg->v_sec));
if (seg->v_sec == NULL)
err(EXIT_FAILURE, "realloc failed");
/*
* Sort the section in order of offset.
*/
for (i = seg->nsec - 1; i > 0; i--) {
s = seg->v_sec[i - 1];
if (sec->off >= s->off) {
seg->v_sec[i] = sec;
break;
} else
seg->v_sec[i] = s;
}
if (i == 0)
seg->v_sec[0] = sec;
}
void
setup_phdr(struct elfcopy *ecp)
{
struct segment *seg;
GElf_Phdr iphdr;
size_t iphnum;
int i;
if (elf_getphnum(ecp->ein, &iphnum) == 0)
errx(EXIT_FAILURE, "elf_getphnum failed: %s",
elf_errmsg(-1));
ecp->ophnum = ecp->iphnum = iphnum;
if (iphnum == 0)
return;
/* If --only-keep-debug is specified, discard all program headers. */
if (ecp->strip == STRIP_NONDEBUG) {
ecp->ophnum = 0;
return;
}
for (i = 0; (size_t)i < iphnum; i++) {
if (gelf_getphdr(ecp->ein, i, &iphdr) != &iphdr)
errx(EXIT_FAILURE, "gelf_getphdr failed: %s",
elf_errmsg(-1));
if ((seg = calloc(1, sizeof(*seg))) == NULL)
err(EXIT_FAILURE, "calloc failed");
seg->addr = iphdr.p_vaddr;
seg->off = iphdr.p_offset;
seg->fsz = iphdr.p_filesz;
seg->msz = iphdr.p_memsz;
seg->type = iphdr.p_type;
STAILQ_INSERT_TAIL(&ecp->v_seg, seg, seg_list);
}
}
void
copy_phdr(struct elfcopy *ecp)
{
struct segment *seg;
struct section *s;
GElf_Phdr iphdr, ophdr;
int i;
STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
if (seg->type == PT_PHDR) {
if (!TAILQ_EMPTY(&ecp->v_sec)) {
s = TAILQ_FIRST(&ecp->v_sec);
if (s->pseudo)
seg->addr = s->lma +
gelf_fsize(ecp->eout, ELF_T_EHDR,
1, EV_CURRENT);
}
seg->fsz = seg->msz = gelf_fsize(ecp->eout, ELF_T_PHDR,
ecp->ophnum, EV_CURRENT);
continue;
}
seg->fsz = seg->msz = 0;
for (i = 0; i < seg->nsec; i++) {
s = seg->v_sec[i];
seg->msz = s->off + s->sz - seg->off;
if (s->type != SHT_NOBITS)
seg->fsz = seg->msz;
}
}
/*
* Allocate space for program headers, note that libelf keep
* track of the number in internal variable, and a call to
* elf_update is needed to update e_phnum of ehdr.
*/
if (gelf_newphdr(ecp->eout, ecp->ophnum) == NULL)
errx(EXIT_FAILURE, "gelf_newphdr() failed: %s",
elf_errmsg(-1));
/*
* This elf_update() call is to update the e_phnum field in
* ehdr. It's necessary because later we will call gelf_getphdr(),
* which does sanity check by comparing ndx argument with e_phnum.
*/
if (elf_update(ecp->eout, ELF_C_NULL) < 0)
errx(EXIT_FAILURE, "elf_update() failed: %s", elf_errmsg(-1));
/*
* iphnum == ophnum, since we don't remove program headers even if
* they no longer contain sections.
*/
i = 0;
STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
if (i >= ecp->iphnum)
break;
if (gelf_getphdr(ecp->ein, i, &iphdr) != &iphdr)
errx(EXIT_FAILURE, "gelf_getphdr failed: %s",
elf_errmsg(-1));
if (gelf_getphdr(ecp->eout, i, &ophdr) != &ophdr)
errx(EXIT_FAILURE, "gelf_getphdr failed: %s",
elf_errmsg(-1));
ophdr.p_type = iphdr.p_type;
ophdr.p_vaddr = seg->addr;
ophdr.p_paddr = seg->addr;
ophdr.p_flags = iphdr.p_flags;
ophdr.p_align = iphdr.p_align;
ophdr.p_offset = seg->off;
ophdr.p_filesz = seg->fsz;
ophdr.p_memsz = seg->msz;
if (!gelf_update_phdr(ecp->eout, i, &ophdr))
err(EXIT_FAILURE, "gelf_update_phdr failed :%s",
elf_errmsg(-1));
i++;
}
}

132
elfcopy/strip.1 Normal file
View File

@ -0,0 +1,132 @@
.\" Copyright (c) 2011 Joseph Koshy. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY JOSEPH KOSHY ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL JOSEPH KOSHY BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $Id: strip.1 2069 2011-10-26 15:53:48Z jkoshy $
.\"
.Dd September 17, 2011
.Os
.Dt STRIP 1
.Sh NAME
.Nm strip
.Nd discard information from ELF objects
.Sh SYNOPSIS
.Nm
.Op Fl d | Fl g | Fl S | Fl -strip-debug
.Op Fl h | Fl -help
.Op Fl -only-keep-debug
.Op Fl o Ar outputfile | Fl -output-file= Ns Ar outputfile
.Op Fl p | Fl -preserve-dates
.Op Fl s | Fl -strip-all
.Op Fl -strip-unneeded
.Op Fl w | Fl -wildcard
.Op Fl x | Fl -discard-all
.Op Fl I Ar format | Fl -input-target= Ns Ar format
.Op Fl K Ar symbol | Fl -keep-symbol= Ns Ar symbol
.Op Fl N Ar symbol | Fl -strip-symbol= Ns Ar symbol
.Op Fl O Ar format | Fl -output-target= Ns Ar format
.Op Fl R Ar sectionname | Fl -remove-section= Ns Ar sectionname
.Op Fl V | Fl -version
.Op Fl X | Fl -discard-locals
.Ar
.Sh DESCRIPTION
The
.Nm
utility is used to discard information from ELF objects.
.Pp
The
.Nm
utility supports the following options:
.Bl -tag -width indent
.It Fl d | Fl g | Fl S | Fl -strip-debug
Remove debugging symbols only.
.It Fl h | Fl -help
Print a help message and exit.
.It Fl -only-keep-debug
Remove all content except that which would be used for debugging.
.It Fl o Ar outputfile | Fl -output-file= Ns Ar outputfile
Write the stripped object to file
.Ar outputfile .
The default behaviour is to modify objects in place.
.It Fl p | Fl -preserve-dates
Preserve the object's access and modification times.
.It Fl s | Fl -strip-all
Remove all symbols.
.It Fl -strip-unneeded
Remove all symbols not needed for further relocation processing.
.It Fl w | Fl -wildcard
Use shell-style patterns to name symbols.
The following meta-characters are recognized in patterns:
.Bl -tag -width "...." -compact
.It Li !
If this is the first character of the pattern, invert the sense of the
pattern match.
.It Li *
Matches any string of characters in a symbol name.
.It Li ?
Matches zero or one character in a symbol name.
.It Li [
Mark the start of a character class.
.It Li \e
Remove the special meaning of the next character in the pattern.
.It Li ]
Mark the end of a character class.
.El
.It Fl x | Fl -discard-all
Discard all non-global symbols.
.It Fl I Ar format | Fl -input-target= Ns Ar format
These options are accepted, but are ignored.
.It Fl K Ar symbol | Fl -keep-symbol= Ns Ar symbol
Keep the symbol
.Ar symbol
even if it would otherwise be stripped.
This option may be specified multiple times.
.It Fl N Ar symbol | Fl -strip-symbol= Ns Ar symbol
Remove the symbol
.Ar symbol
even if it would otherwise have been kept.
This option may be specified multiple times.
.It Fl O Ar format | Fl -output-target= Ns Ar format
Set the output file format to
.Ar format .
For the full list of supported formats, please see the documentation
for function
.Xr elftc_bfd_find_target 3 .
.It Fl R Ar sectionname | Fl -remove-section= Ns Ar sectionname
Remove the section named by the argument
.Ar sectionname .
This option may be specified multiple times.
.It Fl V | Fl -version
Print a version identifier and exit.
.It Fl X | Fl -discard-locals
Remove compiler-generated local symbols.
.El
.Sh DIAGNOSTICS
.Ex -std
.Sh SEE ALSO
.Xr ar 1 ,
.Xr elfcopy 1 ,
.Xr ld 1 ,
.Xr mcs 1 ,
.Xr elf 3 ,
.Xr elftc_bfd_find_target 3 ,
.Xr fnmatch 3

1040
elfcopy/symbols.c Normal file

File diff suppressed because it is too large Load Diff

11
elfdump/Makefile Normal file
View File

@ -0,0 +1,11 @@
# $Id: Makefile 2289 2011-12-04 07:11:47Z jkoshy $
TOP= ..
PROG= elfdump
WARNS?= 6
DPADD= ${LIBELFTC} ${LIBELF}
LDADD= -lelftc -lelf
.include "${TOP}/mk/elftoolchain.prog.mk"

158
elfdump/elfdump.1 Normal file
View File

@ -0,0 +1,158 @@
.\" Copyright (c) 2003 David O'Brien
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD: src/usr.bin/elfdump/elfdump.1,v 1.6 2005/01/18 13:43:48 ru Exp $
.\" $Id: elfdump.1 2069 2011-10-26 15:53:48Z jkoshy $
.\"
.Dd August 25, 2011
.Dt ELFDUMP 1
.Os
.Sh NAME
.Nm elfdump
.Nd "display information about"
.Tn ELF
files
.Sh SYNOPSIS
.Nm
.Fl a | cdeGhiknprsv
.Op Fl S
.Op Fl V
.Op Fl N Ar name
.Op Fl w Ar file
.Ar file ...
.Sh DESCRIPTION
The
.Nm
utility
dumps various information about the specified
.Tn ELF
.Ar file .
.Pp
The options are as follows:
.Bl -tag -width ".Fl w Ar file"
.It Fl a
Dump all information.
.It Fl c
Dump shared headers.
.It Fl d
Dump dynamic symbols.
.It Fl e
Dump ELF header.
.It Fl G
Dump the GOT.
.It Fl h
Dump the hash values.
.It Fl i
Dump the dynamic interpreter.
.It Fl k
Dump the ELF checksum.
.It Fl n
Dump note sections.
.It Fl N Ar name
Only dump the section with the specific
.Ar name .
Archive symbol table can be specified with
the special section name ARSYM.
More than one
.Fl N
option may appear.
.It Fl p
Dump the program header.
.It Fl r
Dump relocations.
.It Fl s
Dump the symbol table.
.It Fl S
Output in the Solaris
.Nm
format.
.It Fl v
Dump the symbol-versioning sections.
.It Fl V
Print a version identifier and exit.
.It Fl w Ar file
Write output to a
.Ar file
instead of the standard output.
.El
.Sh EXIT STATUS
.Ex -std
.Sh EXAMPLES
The following is an example of a typical usage
of the
.Nm
command:
.Pp
.Dl "elfdump -a -w output /bin/ls"
.Pp
To dump the content of '.dynsym' symbol table:
.Pp
.Dl "elfdump -s -N .dynsym /bin/ls"
.Pp
To dump the archive symbol table,
but not the symbol tables of archive members:
.Pp
.Dl "elfdump -s -N ARSYM /usr/lib/libelf.a"
.Pp
To dump the content of .got section and
the symbol-versioning sections in Solaris
.Nm
format:
.Pp
.Dl "elfdump -S -Gv /bin/ls"
.Sh SEE ALSO
.Xr objdump 1 ,
.Xr readelf 1 ,
.Xr elf 3
.Rs
.%A "AT&T Unix Systems Labs"
.%T "System V Application Binary Interface"
.%O http://www.sco.com/developers/gabi/
.Re
.Sh HISTORY
The
.Nm
utility first appeared in
.Fx 5.0 .
.Sh AUTHORS
.An -nosplit
The
.Nm
utility
was written by
.An Jake Burkholder Aq jake@FreeBSD.org .
Later it was rewritten based on the
libelf library.
This
manual page was written by
.An David O'Brien Aq obrien@FreeBSD.org .
.Pp
.An Kai Wang Aq kaiw@FreeBSD.org
rewrote it using the
.Lb libelf
and implemented additional functionality.
.Sh BUGS
Does not fully implement the
.Tn ELF
gABI.

2821
elfdump/elfdump.c Normal file

File diff suppressed because it is too large Load Diff

15
findtextrel/Makefile Normal file
View File

@ -0,0 +1,15 @@
# $Id: Makefile 2069 2011-10-26 15:53:48Z jkoshy $
TOP= ..
PROG= findtextrel
SRCS= findtextrel.c
WARNS?= 6
DPADD= ${LIBELFTC} ${LIBDWARF} ${LIBELF}
LDADD= -lelftc -ldwarf -lelf
MAN1= findtextrel.1
.include "${TOP}/mk/elftoolchain.prog.mk"

104
findtextrel/findtextrel.1 Normal file
View File

@ -0,0 +1,104 @@
.\" Copyright (c) 2010,2011 Joseph Koshy <jkoshy@users.sourceforge.net>
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer
.\" in this position and unchanged.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\" $Id: findtextrel.1 2069 2011-10-26 15:53:48Z jkoshy $
.\"
.Dd August 25, 2011
.Os
.Dt FINDTEXTREL 1
.Sh NAME
.Nm findtextrel
.Nd locate text relocation entries in position independent ELF executables
.Sh SYNOPSIS
.Nm
.Op Fl V
.Op Fl H
.Op Ar
.Sh DESCRIPTION
The
.Nm
utility displays information about text relocations in ELF objects
containing position independent code.
.Pp
Text relocations are usually undesirable because they require that the
text sections of objects be modified at load time, preventing the
sharing of text sections across multiple processes using a dynamic
shared object.
.Pp
Arguments
.Ar
name ELF executables to be examined.
If no files are specified, the
.Nm
utility will examine the file
.Pa a.out
in the current directory.
.Pp
The
.Nm
utility recognizes the following options:
.Bl -tag -width indent
.It Fl H
Print a brief help message.
.It Fl V
Print a version identifier and exit.
.El
.Sh EXIT STATUS
.Ex -std
.Sh EXAMPLES
To list text relocations in an object, use:
.Bd -literal -offset indent
% findtextrel a.out
a.out: ELF object contains text relocation records:
a.out: off: 0x530, func: main, file: a.c, line: 5
.Ed
.Sh DIAGNOSTICS
The
.Nm
may issue the following diagnostics:
.Bl -diag
.It "ELF object is not a DSO/PIE"
The ELF executable specified by argument
.Ar object
was not a position independent executable.
.It "ELF object does not contain a text relocation"
The ELF executable specified by argument
.Ar object
contained no text relocations.
.El
.Sh SEE ALSO
.Xr addr2line 1 ,
.Xr nm 1 ,
.Xr readelf 1
.Sh HISTORY
A
.Nm
utility first appeared in the
.Nm elfutils
toolset from Red Hat, Inc.
.Sh AUTHORS
This implementation of the
.Nm
utility was created by
.An "Kai Wang" Aq kaiwang27@users.sourceforge.net .

417
findtextrel/findtextrel.c Normal file
View File

@ -0,0 +1,417 @@
/*-
* Copyright (c) 2010 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <err.h>
#include <dwarf.h>
#include <fcntl.h>
#include <gelf.h>
#include <getopt.h>
#include <libdwarf.h>
#include <libelftc.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "_elftc.h"
ELFTC_VCSID("$Id: findtextrel.c 2185 2011-11-19 16:07:16Z jkoshy $");
static struct option longopts[] = {
{"help", no_argument, NULL, 'H'},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};
#define USAGE_MESSAGE "\
Usage: %s [options] [files...]\n\
Show text relocations present in position independent code.\n\n\
Options:\n\
-H Print a help message.\n\
-V Print a version identifier and exit.\n"
static void
usage(void)
{
(void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
exit(1);
}
static void
version(void)
{
(void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version());
exit(0);
}
static const char *
find_symbol(const char *fn, Elf *e, Elf_Data *d, GElf_Shdr *sh, uintmax_t off)
{
GElf_Sym sym;
const char *name;
int i, len;
len = d->d_size / sh->sh_entsize;
for (i = 0; i < len; i++) {
if (gelf_getsym(d, i, &sym) != &sym) {
warnx("%s: gelf_getsym() failed: %s", fn,
elf_errmsg(-1));
continue;
}
if (GELF_ST_TYPE(sym.st_info) != STT_FUNC)
continue;
if (off >= sym.st_value && off < sym.st_value + sym.st_size) {
name = elf_strptr(e, sh->sh_link, sym.st_name);
if (name == NULL)
warnx("%s: elf_strptr() failed: %s", fn,
elf_errmsg(-1));
return (name);
}
}
return (NULL);
}
static void
report_textrel(const char *fn, Elf *e, Dwarf_Debug dbg, uintmax_t off,
int *textrel)
{
Dwarf_Die die;
Dwarf_Line *lbuf;
Dwarf_Error de;
Dwarf_Half tag;
Dwarf_Unsigned lopc, hipc, lineno, plineno;
Dwarf_Signed lcount;
Dwarf_Addr lineaddr, plineaddr;
Elf_Scn *scn;
Elf_Data *d;
GElf_Shdr sh;
const char *name;
char *file, *pfile;
int elferr, found, i, ret;
if (!*textrel) {
printf("%s: ELF object contains text relocation records:\n",
fn);
*textrel = 1;
}
printf("%s: off: %#jx", fn, off);
found = 0;
scn = NULL;
while ((scn = elf_nextscn(e, scn)) != NULL) {
if (gelf_getshdr(scn, &sh) == NULL) {
warnx("%s: gelf_getshdr() failed: %s", fn,
elf_errmsg(-1));
continue;
}
if (sh.sh_type != SHT_DYNSYM &&
sh.sh_type != SHT_SYMTAB)
continue;
(void) elf_errno();
if ((d = elf_getdata(scn, NULL)) == NULL) {
elferr = elf_errno();
if (elferr != 0)
warnx("%s: elf_getdata() failed: %s",
fn, elf_errmsg(-1));
continue;
}
if (d->d_size <= 0)
continue;
if ((name = find_symbol(fn, e, d, &sh, off)) != NULL) {
printf(", func: %s", name);
break;
}
}
elferr = elf_errno();
if (elferr != 0)
warnx("%s: elf_nextscn() failed: %s", fn,
elf_errmsg(elferr));
if (dbg == NULL)
goto done;
/*
* More verbose output if debugging information is available.
*/
while ((ret = dwarf_next_cu_header(dbg, NULL, NULL, NULL, NULL, NULL,
&de)) == DW_DLV_OK) {
die = NULL;
while (dwarf_siblingof(dbg, die, &die, &de) == DW_DLV_OK) {
if (dwarf_tag(die, &tag, &de) != DW_DLV_OK)
goto out;
/* XXX: What about DW_TAG_partial_unit? */
if (tag == DW_TAG_compile_unit)
break;
}
if (die == NULL) {
/* Could not find DW_TAG_compile_unit DIE. */
goto out;
}
if (!dwarf_attrval_unsigned(die, DW_AT_low_pc, &lopc, &de) &&
!dwarf_attrval_unsigned(die, DW_AT_high_pc, &hipc, &de)) {
/*
* Check if the address falls into the PC range of
* this CU.
*/
if (off < lopc || off >= hipc)
continue;
} else
continue;
if (dwarf_srclines(die, &lbuf, &lcount, &de) != DW_DLV_OK)
continue;
found = 0;
plineaddr = ~0ULL;
plineno = 0;
pfile = NULL;
for (i = 0; i < lcount; i++) {
if (dwarf_lineaddr(lbuf[i], &lineaddr, &de))
continue;
if (dwarf_lineno(lbuf[i], &lineno, &de))
continue;
if (dwarf_linesrc(lbuf[i], &file, &de))
continue;
if (off == lineaddr) {
found = 1;
goto out;
} else if (off < lineaddr && off > plineaddr) {
lineno = plineno;
file = pfile;
found = 1;
goto out;
}
plineaddr = lineaddr;
plineno = lineno;
pfile = file;
}
}
out:
if (found)
printf(", file: %s, line: %ju", file, lineno);
/*
* Reset internal CU pointer, so we will start from the first CU
* next round.
*/
while (ret != DW_DLV_NO_ENTRY) {
if (ret == DW_DLV_ERROR)
break;
ret = dwarf_next_cu_header(dbg, NULL, NULL, NULL, NULL, NULL,
&de);
}
done:
putchar('\n');
}
static void
examine_reloc(const char *fn, Elf *e, Elf_Data *d, GElf_Shdr *sh, GElf_Phdr *ph,
int phnum, Dwarf_Debug dbg, int *textrel)
{
GElf_Rel rel;
GElf_Rela rela;
int i, j, len;
len = d->d_size / sh->sh_entsize;
for (i = 0; i < len; i++) {
if (sh->sh_type == SHT_REL) {
if (gelf_getrel(d, i, &rel) != &rel) {
warnx("%s: gelf_getrel() failed: %s", fn,
elf_errmsg(-1));
continue;
}
} else {
if (gelf_getrela(d, i, &rela) != &rela) {
warnx("%s: gelf_getrela() failed: %s", fn,
elf_errmsg(-1));
continue;
}
}
for (j = 0; j < phnum; j++) {
if (sh->sh_type == SHT_REL) {
if (rel.r_offset >= ph[j].p_offset &&
rel.r_offset < ph[j].p_offset +
ph[j].p_filesz)
report_textrel(fn, e, dbg,
(uintmax_t) rel.r_offset, textrel);
} else {
if (rela.r_offset >= ph[j].p_offset &&
rela.r_offset < ph[j].p_offset +
ph[j].p_filesz)
report_textrel(fn, e, dbg,
(uintmax_t) rela.r_offset, textrel);
}
}
}
}
static void
find_textrel(const char *fn)
{
Elf *e;
Elf_Scn *scn;
Elf_Data *d;
GElf_Ehdr eh;
GElf_Phdr *ph;
GElf_Shdr sh;
Dwarf_Debug dbg;
Dwarf_Error de;
int elferr, fd, i, phnum, textrel;
e = NULL;
ph = NULL;
dbg = NULL;
if ((fd = open(fn, O_RDONLY)) < 0) {
warn("%s", fn);
return;
}
if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
warnx("%s: elf_begin() failed: %s", fn, elf_errmsg(-1));
goto exit;
}
if (gelf_getehdr(e, &eh) != &eh) {
warnx("%s: gelf_getehdr() failed: %s", fn, elf_errmsg(-1));
goto exit;
}
if (eh.e_type != ET_DYN) {
printf("%s: ELF object is not a DSO/PIE\n", fn);
goto exit;
}
/*
* Search program header for executable segments.
*/
if (eh.e_phnum == 0) {
printf("%s: ELF object does not contain program headers\n",
fn);
goto exit;
}
if ((ph = calloc(eh.e_phnum, sizeof(GElf_Phdr))) == NULL)
err(EXIT_FAILURE, "calloc failed");
phnum = 0;
for (i = 0; (unsigned) i < eh.e_phnum; i++) {
if (gelf_getphdr(e, i, &ph[phnum]) != &ph[phnum]) {
warnx("%s: gelf_getphdr() failed: %s", fn,
elf_errmsg(-1));
continue;
}
if (ph[phnum].p_flags & PF_X)
phnum++;
}
if (phnum == 0) {
printf("%s: ELF object does not contain any executable "
"segment\n", fn);
goto exit;
}
/* Check if debugging information is available. */
if (dwarf_elf_init(e, DW_DLC_READ, NULL, NULL, &dbg, &de))
dbg = NULL;
/*
* Search relocation records for possible text relocations.
*/
textrel = 0;
scn = NULL;
while ((scn = elf_nextscn(e, scn)) != NULL) {
if (gelf_getshdr(scn, &sh) == NULL) {
warnx("%s: gelf_getshdr() failed: %s", fn,
elf_errmsg(-1));
continue;
}
if (sh.sh_type == SHT_REL || sh.sh_type == SHT_RELA) {
(void) elf_errno();
if ((d = elf_getdata(scn, NULL)) == NULL) {
elferr = elf_errno();
if (elferr != 0)
warnx("%s: elf_getdata() failed: %s",
fn, elf_errmsg(-1));
continue;
}
if (d->d_size <= 0)
continue;
examine_reloc(fn, e, d, &sh, ph, phnum, dbg, &textrel);
}
}
elferr = elf_errno();
if (elferr != 0)
warnx("%s: elf_nextscn() failed: %s", fn, elf_errmsg(elferr));
if (!textrel)
printf("%s: ELF object does not contain a text relocation\n",
fn);
exit:
if (dbg)
dwarf_finish(dbg, &de);
if (ph)
free(ph);
if (e)
(void) elf_end(e);
close(fd);
}
int
main(int argc, char **argv)
{
int i, opt;
if (elf_version(EV_CURRENT) == EV_NONE)
errx(EXIT_FAILURE, "elf_version(): %s", elf_errmsg(-1));
while ((opt = getopt_long(argc, argv, "HV", longopts, NULL)) != -1) {
switch (opt) {
case 'H':
usage();
case 'V':
version();
default:
usage();
}
}
argv += optind;
argc -= optind;
if (argc > 0)
for (i = 0; i < argc; i++)
find_textrel(argv[i]);
else
find_textrel("a.out");
exit(0);
}

20
isa/Makefile Normal file
View File

@ -0,0 +1,20 @@
# $Id: Makefile 2903 2013-01-16 12:35:50Z jkoshy $
TOP= ..
PROG= isa
SRCS= isa.c
LSRC=
YSRC=
LDADD= -lelftc
ISA= avr.isa
MAN= isa.1 isa.5
check-specifications: .PHONY
.for f in ${ISA}
${.OBJDIR}/${PROG} -n --query ${f}
.endfor
.include "${TOP}/mk/elftoolchain.prog.mk"

280
isa/avr.isa Normal file
View File

@ -0,0 +1,280 @@
;
; $Id: avr.isa 2899 2013-01-16 05:17:02Z jkoshy $
;
; An instruction set description for Atmel AVR(TM) 8 bit CPUs.
;
; * Most instructions are 16 bit wide, except a few that use an
; additional 16-bit offset field.
; * There are 32 general purpose registers which are 8-bit wide.
; Three pairs of registers are used for 16bit memory addressing:
; X (r27:r26), Y (r29:r28), and Z (r31:r30).
; * Some instructions operate on limited subsets of these registers:
; - The 'movw' instruction operates on register pairs.
; - Some instructions only operate on subsets of the register file.
arch avr
cpus
core = [ AT90S1200 ATtiny11 ATtiny12 ATtiny15 ATtiny28 ]
core8k = core ++ [ AT90S2313 AT90S2323 ATtiny22 AT90S2333
AT90S2343 AT90S4414 AT90S4433 AT90S4434 AT90S8515
AT90C8534 AT90S8535 ATtiny26 ATmega8515 ]
core128k = core8k ++ [ ATmega103 ATmega603 AT43USB320 AT76C711 ]
enhancedcore = core ++ [ ATmega8 ATmega83 ATmega85 ]
; TODO fill in the rest.
; The instruction stream has two types of tokens:
token i(16) ; a 16 bit instruction.
offset(16) ; a 16 bit offset
; The 32 source registers are encoded using a combination of a 4-bit
; and a 1-bit field.
let Rsrclow = i[3:0]
Rsrchighbit = i[9]
Rsrc = Rsrchighbit & Rsrclow
where Rsrc[4] = Rsrchighbit
Rsrc[3:0] = Rsrclow
names [ R%n | n = 0..31 ]
Rsrcpair = i[3:0] ; Source register pairs.
names [ R%n | n = 0..31, n % 2 == 0 ]
Rdst = i[8:4] ; The 32 dst registers use 5 contiguous bits.
names [ R%n | n = 0..31 ]
Rdstpair = i[7:4] ; destination register pairs
names [ R%n | n = 0..31, n %2 == 0 ]
; Some instructions work on the 16 higher numbered registers.
Rsrchigh = i[7:4]
names [ R%n | n = 16..31 ]
Rdsthigh = i[7:4]
names [ R%n | n = 16..31 ]
; Registers used for the MUL instructions (R16-23).
Rmulsrc = i[2:0]
names [ R%n | n = 16..23 ]
Rmuldst = i[6:4]
names [ R%n | n = 16..23 ]
; 8 bit immediate value.
let Khigh = i[11:8]
Klow = i[ 3:0]
K = Khigh & Klow
where K[8:4] = Khigh
K[3:0] = Klow
; call or jmp to an absolute location
let jmpcallbit = i[1]
Jmpcall loc = i[15:9] = 0b1001010 & i[3:2] = 0b11 &
i[8:4] = loc[21:17] & i[0] = loc[16]
<+> ; next location
offset[15:0] = loc[15:0]
in
call %loc <=> Jmpcall loc & jmpcallbit = 1
jmp %loc <=> Jmpcall loc & jmpcallbit = 0
; Immediate operations on the high registers.
let immediateops@[ sbci subi sbr cbr ] = [ i[13:12] = n | n = 0..3 ]
with i[15:14] = 0b01
@immediateops %Rdsthigh, %K <=> &*
; The CPI (Compare Immediate) instruction has a different encoding.
cpi %Rdsthigh, %K <=> i[15:12] = 0b0011 &*
; Move register pair.
movw %Rdstpair, %Rsrcpair <=> i[15:8] = 0b00000001 &*
; 8x8 -> 16 bit signed multiply.
muls %Rdsthigh, %Rsrchigh <=> i[15:8] = 0b00000010 &*
; Unsigned multiply.
mul %Rdst, %Rsrc <=> i[15:10] = 0b100111 &*
; Fractional multiply instructions.
with i[15:8] = 0b00000011
fmulsu %Rmuldst, %Rmulsrc <=> i[7,3] = [1,1] &*
fmuls %Rmuldst, %Rmulsrc <=> i[7,3] = [1,0] &*
fmul %Rmuldst, %Rmulsrc <=> i[7,3] = [0,1] &*
mulsu %Rmuldst, %Rmulsrc <=> i[7,3] = [0,0] &*
; 2-operand instructions operating on all 32 registers.
let OpTwo@[ cpc sbc add cpse cp sub adc and eor or mov ] in
cpc = i[15:10] = 0b000001
with i[15:12] = 0b0010
[ and eor or mov ] = [ i[11:10] = n | n = 0..3 ]
with i[15:12] = 0b0001
[ cpse cp sub adc ] = [ i[11:10] = n | n = 0..3 ]
with i[15:11] = 0b00001
[ sbc add ] = [ i[10] = n | n = 0..1 ]
in
@OpTwo %Rdst, %Rsrc <=> &*
let OpOne = [ com neg swap inc asr lsr ror ]
with i[15:9] = 0b1001010 & i[3] = 0
[ com neg swap inc _ asr lsr ror ] = [ i[2:0] = n | n = 0..7 ]
in
@OpOne %Rdst <=> &*
let bitno = i[2:0]
let clear = i[7] in
with i[15:8] = 0b10010100 & i[3:0] = 0b1000
bclr %bitno <=> clear = 1 &*
bset %bitno <=> clear = 0 &*
; Additional aliases.
let statusbit = i[6:4]
names [ "C" "Z" "N" "V" "S" "H" "T" "I" ]
cl%statusbit <=> bclr & bitno = statusbit
se%statusbit <=> bset & bitno = statusbit
; NOP
nop <=> i[15:0] = 0
let loadstore = i[9]
; Index load/store with offset
let lddlow = i[2:0]
lddmid = i[11:10]
lddhigh = i[13]
Lddoffset = lddlow & lddmid & lddhigh
where Lddoffset[5] = lddhigh
Lddoffset[4:3] = lddmid
Lddoffset[2:0] = lddlow
yz = i[3] ; Y/Z bit for LDD with offset
names [ "Z" "Y" ]
with i[15:14] = 0b10 & i[12] = 0b0
ldd %Rdst, %yz "+" %Lddoffset <=> loadstore = 0 &*
std %Rdst, %yz "+" %Lddoffset <=> loadstore = 1 &*
; Indexed load/store with increment & decrement
let xyz = i[3:2] ; X/Y/Z for LD ops
names [ "Z" _ "Y" "X" ]
auto = i[0:1]
with i[15:10] = 0b100100 ; prefix for indexed loads
ld %Rdst, %xyz <=> loadstore = 0 & auto = 0 &*
ld %Rdst, %xyz+ <=> loadstore = 0 & auto = 1 &*
ld %Rdst, -%xyz <=> loadstore = 0 & auto = 2 &*
st %xyz, %Rdst <=> loadstore = 1 & auto = 0 &*
st %xyz+, %Rdst <=> loadstore = 1 & auto = 1 &*
st -%xyz, %Rdst <=> loadstore = 1 & auto = 2 &*
; The 'andi' instruction is 'cbi' with a negated constant.
andi %Rdsthigh, %Kcomp <=> cbr & Rdsthigh & K = ~Kcomp
; The 'ori' instruction is an alias for 'sbr'.
ori %Rdsthigh, %K <=> sbr &*
; Single operand instructions implemented using two operand ones.
clr %Rdst <=> eor & Rsrc = Rdst & Rdst
lsl %Rdst <=> add & Rsrc = Rdst & Rdst
rol %Rdst <=> adc & Rsrc = Rdst & Rdst
tst %Rdst <=> and & Rsrc = Rdst & Rdst
with i[15:9] = 0b1001010 & i[7:0] = 0b0001001
ijmp <=> indircallbit = 0 & eibit = 0
icall <=> indircallbit = 1 & eibit = 0
eijmp <=> indircallbit = 0 & eibit = 1
eicall <=> indircallbit = 1 & eibit = 1
where indircallbit = i[7]
eibit = i[4]
with i[15:8] = 0b10010101 & i[3:0] = 0b1000
let splops = i[7:4]
miscops@[ ret reti sleep break wdr lpm elpm spm ] =
[ splops = [ 0 1 8 9 10 12 13 14 ] ]
in
@miscops <=> &*
; Load program memory has two variants.
lpm <=> i[15:0] = 0b1001010111001000 ; load to R0
lpm %Rdst,Z%zincr <=> i[15:9] = 0b1001000 & i[3:1] = 0b010 &*
where zincr = i[0] names [ "" "+" ]
; Store program memory.
spm <=> i[15:0] = 0b1001010111101000
; Decrement register.
dec %Rdst <=> i[15:9] = 0b1001010 & i[3:0] = 0b1010 &*
; DES round %des, operates on R0..R15
let des = i[7:4] in
des %des <=> i[15:8] = 0b10010100 & i[3:0] = 0b1011 &*
; Add/Sub register pairs with an immediate
let addsub = i[8]
Rdstimm = i[5:4]
names [ R24 R26 R28 R30 ]
Kimm6high = i[7:6]
Kimm6low = i[3:0]
Kimm6 = Kimm6high & Kimm6low
where Kimm6[5:4] = Kimm6high
Kimm6[3:0] = Kimm6low
with i[15:9] = 0b1001011
adiw %Rdstimm, %Kimm6 <=> addsub = 0 &*
sbiw %Rdstimm, %Kimm6 <=> addsub = 1 &*
; Operations on bits in I/O registers.
let bitops@[ cbi sbic sbi sbis ] = [ instr[9:8] = n | n = 0..3 ]
ioaddr = i[7:3]
with i[15:10] = 0b100110
@bitops %ioaddr, %bit <=> &*
; IN/OUT operations
let inout = i[11]
Alow = i[3:0]
Ahigh = i[10:9]
A = Ahigh & Alow
with i[15..12] = 0b1011
in %Rdst, %A <=> inout = 0 & *
out %Rst, %A <=> inout = 1 & *
; Relative jmp/call
let reljmpcall = i[12]
reloffset = i[11:0]
with i[15:13] = 0b110
rjmp %label <=> reljmpcall = 0 & reloffset = (label - . - 1)
rcall %label <=> reljmpcall = 1 & reloffset = (label - . - 1)
; Load Immediate
ldi %Rdsthigh, %K <=> i[15:12] = 0b1110 &*
; Conditional branches
let clearedorset = i[10]
condoffset = i[9:3]
with i[15:11] = 0b11110
brbs %bitno, %label <=> clearedorset = 0 & bitno &
condoffset = (label - . - 1)
brbc %bitno, %label <=> clearedorset = 1 & bitno &
condoffset = (label - . - 1)
; Aliases
brcs %l => brbs & bitno = 0 & label = l
brlo %l => brbs & bitno = 0 & label = l
breq %l => brbs & bitno = 1 & label = l
brmi %l => brbs & bitno = 2 & label = l
brvs %l => brbs & bitno = 3 & label = l
brlt %l => brbs & bitno = 4 & label = l
brhs %l => brbs & bitno = 5 & label = l
brts %l => brbs & bitno = 6 & label = l
brie %l => brbs & bitno = 7 & label = l
brcc %l => brbc & bitno = 0 & label = l
brsh %l => brbc & bitno = 0 & label = l
brne %l => brbc & bitno = 1 & label = l
brpl %l => brbc & bitno = 2 & label = l
brvc %l => brbc & bitno = 3 & label = l
brge %l => brbc & bitno = 4 & label = l
brhc %l => brbc & bitno = 5 & label = l
brtc %l => brbc & bitno = 6 & label = l
brid %l => brbc & bitno = 7 & label = l
; BLD/BST
let bldst = i[9]
with i[15:10] = 0b111110 & i[3] = 0
bld %Rdst, %bitno <=> bldst = 0 &*
bst %Rdst, %bitno <=> bldst = 1 &*
; SBRC/SBRS
let setclr = i[9]
with i[15:10] = 0b111111 & i[3] = 0
sbrc %Rdst, %bit <=> setclr = 0 &*
sbrc %Rdst, %bit <=> setclr = 1 &*

248
isa/isa.1 Normal file
View File

@ -0,0 +1,248 @@
.\" Copyright (c) 2012,2013 Joseph Koshy.
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
\" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\" $Id: isa.1 2889 2013-01-13 15:36:04Z jkoshy $
.\"
.Dd January 13, 2013
.Os
.Dt ISA 1
.Sh NAME
.Nm isa
.Nd instruction set analyser
.Sh SYNOPSIS
.Nm
.Op Fl a Ar architecture | Fl -arch Ns = Ns Ar architecture
.Op Fl c Ar cpu | Fl -cpu Ns = Ns Ar cpu
.Op Fl h | Fl -help
.Op Fl i Ar filename | Fl -input Ns = Ns Ar filename
.Op Fl n | Fl -dry-run
.Op Fl o Ar filename | Fl -output Ns = Ns Ar filename
.Op Fl p Ar string | Fl -prefix Ns = Ns Ar string
.Op Fl s Ar filename | Fl -spec Ns = Ns Ar filename
.Op Fl q | Fl -quiet
.Op Fl v | Fl -verbose
.Op Fl D | Fl -decode
.Op Fl E | Fl -encode
.Op Fl L | Fl -list-instructions
.Op Fl N Ar number | Fl -ntests Ns = Ns Ar number
.Op Fl Q | Fl -query
.Op Fl R Ar number | Fl -random-seed Ns = Ns Ar number
.Op Fl T | Fl -test
.Op Fl V | Fl -version
.Ar
.Sh DESCRIPTION
The
.Nm
utility is used to generate instruction stream encoders and decoders
from a textual description of a CPU instruction set.
.Pp
The
.Nm
utility supports three operational modes, as specified by the use of
the
.Fl D ,
.Fl E
or
.Fl Q
options:
.Bl -tag -width indent
.It Cm Decode
.Pq Fl D | Fl -decode
In this mode, the
.Nm
utility transforms source code, expanding match pattern between the
tokens
.Dq Li "[ISA["
and
.Dq Li "]ISA]"
into the appropriate code for matching instruction streams.
The section
.Sx "Matching Instructions"
describes the decode functionality in greater depth.
.It Cm Encode
.Pq Fl E | Fl -encode
In this mode, the
.Nm
utility generates C code to emit binary instruction streams.
.It Cm Query
.Pq Fl Q | Fl -query
In this mode, the
.Nm
utility is used to retrieve information from instruction set
specifications.
.El
.Pp
If no operational mode is specified, a default of
.Cm Query
will be used.
.Pp
Instruction set specifications may be specified using the
.Fa a
option, or by using the command line arguments
.Ar .
.Pp
The
.Nm
utility accepts the following options:
.Bl -tag -width indent
.It Fl a Ar architecture | Fl -arch Ns = N Ar architecture
Use instruction set specifications specified by the argument
.Ar architecture .
The
.Nm
utility will look for these specifications in the locations
specified by the environment variable
.Ev ISAPATH ,
in addition to a built-in search location.
The default architecture is that for the host the
.Nm
utility is being executed on.s
.It Fl c Ar cpu | Fl -cpu Ns = Ns Ar cpu
Generate encoders and decoders for the specific instruction set
variant supported by CPU
.Ar cpu .
This option may be specified multiple times.
If the argument
.Ar cpu
starts with a minus, the CPU specified will be removed from list of
CPUs to be supported.
.It Fl h | Fl -help
Print a help message and exit.
.It Fl i Ar filename | Fl -input Ns = Ns Ar filename
When generating a decoder, read the source to be expanded from the
file named in the argument
.Ar filename .
If an input file is not specified, the
.Nm
utility will read from its standard input.
.It Fl n | Fl -dry-run
Exit without creating any output after checking inputs for errors.
.It Fl o Ar filename | Fl -output Ns = Ns Ar filename
When generating encoders and decoders, send the output to the file
specified by the argument
.Ar filename .
If an output file is not specified, the
.Nm
utility will write to its standard output.
.It Fl p Ar string | Fl -prefix Ns = Ns Ar string
When in encode mode, use the string in argument
.Ar string
as a prefix for generated symbols.
.It Fl q | Fl -quiet
Suppress warning messages.
.It Fl s Ar filename | Fl -spec Ns = Ns Ar filename
Read an instruction set specification from the file named by argument
.Ar filename .
This option may be specified multiple times, in which case the
.Nm
utilitiy behaves as if the specifications had been concatenated in the
sequence specified.
.It Fl v | Fl -verbose
Increase the verbosity level.
This option may be specified multiple times.
.It Fl D | Fl -decode
Transform sources expanding match patterns in source code to
lower-level instruction stream decoding code.
By default, the
.Nm
utility will read from standard input and write to standard output,
unless otherwise specified by the
.Fl i
and
.Fl o
options.
.It Fl E | Fl -encode
Build an instruction stream encoder.
.It Fl L | Fl -list-instructions
When in query mode, generate a list of all known instructions.
.It Fl N Ar number | Fl -ntests Ns = Ns Ar number
When in query mode, specify the number of test sequences to be
generated if the
.Fl -T | Fl -test
option was specified.
.It Fl Q | Fl -query
Retrieve information about an instruction set.
.It Fl R Ar number | Fl -random-seed Ns = Ns Ar number
Use the argument
.Ar number
as the seed for pseudorandom number generation.
If this option is not specified, the
.Nm
utility will initialize the pseudorandom number generator in an
implementation-defined manner.
.It Fl T | Fl -test
Generate instruction sequences for use in testing tools such as
assemblers.
.It Fl V | Fl -version
Print a version identifier and exit.
.El
.Sh ENVIRONMENT
The behavior of the
.Nm
utility is affected by the following environemnt variables:
.Bl -tag
.It Ev ISAPATH
Specifies a colon-separated set of directories tp be used when
searching for instruction specifications.
.El
.Sh FILES
.Bl -tag -width indent
.It Pa /usr/share/isa/
The default location for instruction set specifications.
.El
.Sh EXAMPLES
To check the instruction specifications in file
.Pa spec.isa ,
use:
.D1 isa -n "spec.isa"
.Pp
To expand instruction decoding templates in the file
.Pa a.m ,
assuming a generic
.Tn AVR
CPU, and generating a C source file, use:
.D1 isa -a avr -D < a.m > a.c
.Sh SEE ALSO
.Xr elf 3 ,
.Xr elf 5 ,
.Xr isa 5
.Sh HISTORY
The
.Nm
utility is scheduled to appear in a future release from the
Elftoolchain project.
.\" TODO Reword the above when the target release is finalized.
.Sh AUTHORS
The
.Xr isa 1
utility was written by
.An Joseph Koshy
.Aq jkoshy@users.sourceforge.net .
.Sh BUGS
The
.Nm
utility is wildly unstable at this point of time.
If you intend to use this utility, please get in touch with the
project's developers at
.Aq elftoolchain-developers@lists.sourceforge.net .

366
isa/isa.5 Normal file
View File

@ -0,0 +1,366 @@
.\" Copyright (c) 2013 Joseph Koshy.
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
.\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
.\" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\" $Id: isa.5 2900 2013-01-16 12:27:01Z jkoshy $
.\"
.Dd January 16, 2013
.Os
.Dt ISA 1
.Sh NAME
.Nm isa
.Nd input file format for the isa utility
.Sh DESCRIPTION
The
.Nm
utility is used to generate instruction stream encoders and decoders
from a textual description of a machine instruction set.
This manual page documents the form of the textual description
accepted by the
.Nm
utility.
.Ss Basic Concepts
A machine instruction is composed of one
.Em tokens ,
each kind of token having a defined width.
Simple RISC-like instruction sets have instructions that use 1 or 2
tokens, typically an instruction word and an optional immediate field.
More complex CISC instruction sets may use many more kinds of tokens.
.Pp
Each token is made up of
.Em fields ,
for example, an instruction token could be made up of an opcode field,
additional fields naming registers, fields containing flags, immediate
values and so on.
Fields may be named using the
.Cm let
directive, or can be unnamed.
The bitslice operator
.Pq Li \&[]
can be used to denote specific portions of a token.
.Pp
Non-overlapping fields are grouped together into
.Em fragments .
Fragments may be composed using the
.Dq "&"
operator.
The textual form of a fragment may be specified using
the
.Li names
directive.
.Pp
A set of fragments that fully specifies each bit in a token is
said to be
.Sq complete .
Only complete fragment sets can be emitted.
.Pp
.Ss Input Syntax
The semicolon
.Dq "\&;"
introduces a comment.
All text from the semicolon to the end of the line is ignored.
.Pp
The language uses indentation to specify scope (i.e., it uses the
offside rule), as in the
.Ic Python
and
.Ic Haskell
programming languages.
.Ss Operators
.Bl -tag
.It "Composing Fragments"
The
.Dq Li \&&
operator is used to join fragments, forming a larger fragment.
For example, to specify a fragment that is comprised of two
previously named fragments
.Ar Rtop
and
.Ar Rbottom ,
use:
.Bd -literal -offset indent
Rtop & Rbottom
.Ed
.It "Generators"
A generator expression has the form
.Li [ Ar expr1 Ns Li \&| Ns Ar expr2 Ns \&... Ns Li ]
and denotes a sequence of values
.Va expr1 ,
where the additional expressions
.Va expr2
serve to define the range of values generated.
Any
.Dq Li \&% Ns
-escapes in
.Va expr1
are expanded.
For example,
.Dl [ R%n | n = 0..31 ]
generates the sequence
.Li R0 ,
.Li R1 ,
\&... ,
.Li R31 .
.It "Numeric Ranges"
The notation
.Dq \&..
denotes a numeric range.
For example,
.Dl 0..(2^16-1)
represents the numbers 0 to 65535, inclusive.
.It Sequences
Sequences of items are bracketed by square brackets
.Dq "\&["
and
.Dq "\&]" .
For example,
.Dl "let n = [ a b c d ]"
Sequences can be given a local name using the
.Va name
.Li @
.Va sequence
syntax, for example:
.Dl bar@[ 1 2 3 ]
defines
.Va bar
as a local name for the expression [ 1 2 3 ].
.Pp
The
.Dq Li \&++
operator is used to concatenate sequences.
These sequences must be of the same type.
.It "Sequencing Tokens"
The
.Dq Li \&<+>
operator separates tokens in sequence.
For example, to specify an instruction that has two tokens T1 and T2
in sequence, use:
.Bd -literal -offset indent
\&..the definition of T1..
<+>
\&..the definition of T2..
.Ed
.It Slices
Slices may be specified using the slice notation, namely
.Ar name Ns
.Li \&[ Ns
.Ar highbit Ns
.Li \&: Ns
.Ar lowbit Ns
.Li \&] ,
where
.Ar highbit
and
.Ar lowbit
are inclusive zero-based indices and
.Ar name
is the name of a token.
.Bd -literal -offset indent
let Rsrc = instruction[3:0]
.Ed
.Pp
Sparse slices may be specified by separating slice expressions using
commas, for example bit 7 and 5 of the
.Va ifield
token may be specified using:
.Dl ifield[7,5]
.It "Specifying Assembly Formats"
The
.Dq Li \&<=>
infix operator is used to specify assembly language syntax and its
mapping to sequences of fragments defined earlier, see the section
.Sx "Defining Assembly Syntax" .
.Pp
The
.Dq Li \&&*
operator indicates that all the named fragments in the LHS (the
assembly syntax side) of the
.Dq Li \&<=>
operator should be treated as being present on the RHS.
This operator allows instructions that have a simple one-to-one
mapping between their assembly language definition and instruction
encoding to be described succinctly.
For example:
.Bd -literal -offset indent
muls %Rd, %Rs <=> i[15:8] = 0b00000010 &*
.Ed
.El
.Ss Language Constructs
The input language has the following constructs:
.Bl -tag -width indent
.It Li arch Ar string
Specifies the name of the instruction set architecture being
processed.
.Bd -literal -offset indent
arch myarch
.Ed
.It Li cpus
Starts a block naming CPU identifiers.
Specific instructions or groups of instructions may be flagged
as being supported on sets of the CPUs so declared.
.Bd -literal -offset indent
cpus
basic = [ CPU1 CPU2 ]
advanced = basic ++ [ CPU3 ]
.Ed
.It Li token Ar name "(" Ar width ")"
Defines a token with name
.Ar name
and width
.Ar width .
For example, to define a 16 bit named
.Ar i
(short for
.Dq instruction ) ,
and a 8 bit offset token named
.Ar o ,
use:
.Bd -literal -offset indent
token i(16) ; a comment here
o(8)
.Ed
.It Li let Ar name [ Ar params ] "=" Ar expression
Declare
.Ar name
as being the equivalent of
.Ar expression .
.It Li names Ar generator-expression
Defines the textual representation for a fragment.
For example,
.Bd -literal -offset indent -compact
let Rsrc = i[3:0]
names [ R%n | n = 0..7 ]
.Ed
specifies that a value of 0 for fragment
.Va Rsrc
should be shown as
.Li R0 ,
and so on.
Conversely, when assembing text, the string
.Dq R15
would be translated to a fragment value of 15.
.It Li where Ar name [ Ar params ] = Ar expression
Like the
.Li let
statement, a
.Li where
statement introduces local definitions, except that the scope of these
definitions is the statement preceding the
.Li where
keyword.
Example:
.Bd -literal -offset indent
let Kimm6 = Kimm6high & Kimm6low
where Kimm6[5:4] = Kimm6high
Kimm6[3:0] = Kimm6low
.Ed
.It Li with Ar fragment-definition
Defines fragment assignments that hold for statements in the scope of
the
.Li with
statement.
For example,
.Bd -literal -offset indent
with i[15:8] = 0b00000011
fmulsu %Rd, %Rs <=> i[7,3] = [1,1] &*
.Ed
.El
.Ss Defining Assembly Syntax
Assembly syntax is described using the
.Li \&<=>
operator.
The form of the operator is
.Bd -ragged -offset indent
assembler-text
.Li \&<=>
.Va fragment
.Li \&&
.Va fragment
.Li & \&...
.Ed
.Pp
The RHS of the
.Li \&<=>
operator must specify a
.Sq complete
fragment set, i.e., no bits should be unspecified in any of the tokens
used in the RHS.
The LHS of the
.Li \&<=>
operator consists of literal text interspersed by fragment names.
Fragment names are prefixed by the
.Sq \&%
character.
These fragment names in the LHS may refer to fragment names defined
earlier, or may be new names that are local to the current definition.
.Pp
For example, the following definition defines an instruction with
mnemonic
.Dq Li rjmp .
.Bd -literal -offset indent
let reloffset = i[11:0]
reljmpcall = i[12]
in
with i[15:13] = 0b110
rjmp %label <=> reljmpcall = 0 & reloffset = (label - . - 1)
.Ed
.Pp
In this definition, the field
.Va label
is a local fragment, one that is used to compute the value of the
.Va reloffset
field in the instruction.
In the RHS, the
.Va reljmpcall
bit is defined as being 0.
The rest of the bits in the token
.Va i
are specified by the enclosing
.Li with
statement.
.Sh SEE ALSO
.Xr elf 3 ,
.Xr elf 5 ,
.Xr isa 1
.Sh HISTORY
The
.Nm
utility is scheduled to appear in a future release from the
Elftoolchain project.
.\" TODO Reword the above when the target release is finalized.
.Sh AUTHORS
The
.Xr isa 1
utility was written by
.An Joseph Koshy
.Aq jkoshy@users.sourceforge.net .
.Sh BUGS
The
.Nm
utility is
.Ud
The input format documented in this manual is likely to change
in the future.
If you intend to use this utility, please get in touch with the
project's developers at
.Aq elftoolchain-developers@lists.sourceforge.net .

286
isa/isa.c Normal file
View File

@ -0,0 +1,286 @@
/*-
* Copyright (c) 2012,2013 Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/queue.h>
#include <err.h>
#include <getopt.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libelftc.h>
#include "_elftc.h"
ELFTC_VCSID("$Id: isa.c 2934 2013-03-30 01:40:49Z jkoshy $");
/*
* Option handling.
*/
enum isa_mode {
ISA_MODE_DECODE,
ISA_MODE_ENCODE,
ISA_MODE_QUERY
};
enum isa_submode {
ISA_SUBMODE_GENERATE_TESTS,
ISA_SUBMODE_LIST_INSTRUCTIONS
};
#define ISA_OPT_DRY_RUN 0x0001
#define ISA_OPT_NO_WARNINGS 0x0002
#define ISA_OPT_VERBOSE 0x0004
/* Record a option. */
struct isa_option {
const char *isa_option;
SLIST_ENTRY(isa_option) isa_next;
};
struct isa_config {
unsigned int isa_flags;
enum isa_mode isa_mode;
enum isa_submode isa_submode;
int isa_ntests;
int isa_seed;
const char *isa_arch;
const char *isa_input;
const char *isa_output;
const char *isa_prefix;
SLIST_HEAD(,isa_option) isa_cpus;
SLIST_HEAD(,isa_option) isa_specs;
};
#define ISA_MAX_LONG_OPTION_LENGTH 64
static struct option isa_long_options[] = {
{ "arch", required_argument, NULL, 'a' },
{ "cpu", required_argument, NULL, 'c' },
{ "decode", no_argument, NULL, 'D' },
{ "dry-run", no_argument, NULL, 'n' },
{ "encode", no_argument, NULL, 'E' },
{ "help", no_argument, NULL, 'h' },
{ "input", required_argument, NULL, 'i' },
{ "list-instructions", no_argument, NULL, 'L' },
{ "ntests", required_argument, NULL, 'N' },
{ "output", required_argument, NULL, 'o' },
{ "prefix", required_argument, NULL, 'p' },
{ "query", no_argument, NULL, 'Q' },
{ "quiet", no_argument, NULL, 'q' },
{ "random-seed", required_argument, NULL, 'R' },
{ "spec", required_argument, NULL, 's' },
{ "test", no_argument, NULL, 'T' },
{ "verbose", no_argument, NULL, 'v' },
{ "version", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0 }
};
static const char *isa_usage_message = "\
usage: %s [options] [command] [specfiles]...\n\
Process an instruction set specification.\n\
\n\
Supported values for 'command' are:\n\
decode Build an instruction stream decoder.\n\
encode Build an instruction stream encoder.\n\
query (default) Retrieve information about an instruction set.\n\
\n\
Supported global options are:\n\
-a ARCH | --arch ARCH Process instruction specifications for ARCH.\n\
-c CPU | --cpu CPU Process instruction specifications for CPU.\n\
-n | --dry-run Exit after checking inputs for errors.\n\
-s FILE | --spec FILE Read instruction specifications from FILE.\n\
-q | --quiet Suppress warning messages.\n\
-v | --verbose Be verbose.\n\
-V | --version Display a version identifier and exit.\n\
\n\
Supported options for command 'decode' are:\n\
-i FILE | --input FILE Read source to be expanded from FILE.\n\
-o FILE | --output FILE Write generated output to FILE.\n\
\n\
Supported options for command 'encode' are:\n\
-o FILE | --output FILE Write generated output to FILE.\n\
-p STR | --prefix STR Use STR as a prefix for generated symbols.\n\
\n\
Supported options for command 'query' are:\n\
-L | --list-instructions Generate a list of all known instructions.\n\
-N NUM | --ntests NUM Specify the number of test sequences generated.\n\
-R N | --random-seed N Use N as the random number generator seed.\n\
-T | --test Generate test sequences.\n\
";
void
isa_usage(int iserror, const char *message, ...)
{
FILE *channel;
va_list ap;
channel = iserror ? stderr : stdout;
if (message) {
va_start(ap, message);
(void) vfprintf(channel, message, ap);
va_end(ap);
}
(void) fprintf(channel, isa_usage_message, ELFTC_GETPROGNAME());
exit(iserror != 0);
}
void
isa_unimplemented(int option, int option_index, struct option *options_table)
{
char msgbuf[ISA_MAX_LONG_OPTION_LENGTH];
if (option_index >= 0)
(void) snprintf(msgbuf, sizeof(msgbuf), "\"--%s\"",
options_table[option_index].name);
else
(void) snprintf(msgbuf, sizeof(msgbuf), "'-%c'",
option);
errx(1, "ERROR: option %s is unimplemented.", msgbuf);
}
struct isa_option *
isa_make_option(const char *arg)
{
struct isa_option *isa_opt;
if ((isa_opt = malloc(sizeof(*isa_opt))) == NULL)
return (NULL);
isa_opt->isa_option = optarg;
return (isa_opt);
}
int
main(int argc, char **argv)
{
int option, option_index;
struct isa_option *isa_opt;
struct isa_config config;
(void) memset(&config, 0, sizeof(config));
config.isa_mode = ISA_MODE_QUERY;
config.isa_arch = config.isa_input = config.isa_output =
config.isa_prefix = NULL;
SLIST_INIT(&config.isa_cpus);
SLIST_INIT(&config.isa_specs);
for (option_index = -1;
(option = getopt_long(argc, argv, "a:c:hi:no:p:qs:vDELN:QR:TV",
isa_long_options, &option_index)) != -1;
option_index = -1) {
switch (option) {
case 'h':
isa_usage(0, NULL);
break;
case 'V':
(void) printf("%s (%s)\n", ELFTC_GETPROGNAME(),
elftc_version());
exit(0);
break;
case 'a':
config.isa_arch = optarg;
break;
case 'c':
if ((isa_opt = isa_make_option(optarg)) == NULL)
goto error;
SLIST_INSERT_HEAD(&config.isa_cpus, isa_opt, isa_next);
break;
case 'i':
config.isa_input = optarg;
break;
case 'n':
config.isa_flags |= ISA_OPT_DRY_RUN;
break;
case 'o':
config.isa_output = optarg;
break;
case 'p':
config.isa_prefix = optarg;
break;
case 'q':
config.isa_flags |= ISA_OPT_NO_WARNINGS;
break;
case 's':
if ((isa_opt = isa_make_option(optarg)) == NULL)
goto error;
SLIST_INSERT_HEAD(&config.isa_specs, isa_opt,
isa_next);
break;
case 'v':
config.isa_flags |= ISA_OPT_VERBOSE;
break;
case 'D':
config.isa_mode = ISA_MODE_DECODE;
break;
case 'E':
config.isa_mode = ISA_MODE_ENCODE;
break;
case 'L':
config.isa_submode = ISA_SUBMODE_LIST_INSTRUCTIONS;
break;
case 'N':
config.isa_ntests = atoi(optarg);
break;
case 'Q':
config.isa_mode = ISA_MODE_QUERY;
break;
case 'R':
config.isa_seed = atoi(optarg);
break;
case 'T':
config.isa_submode = ISA_SUBMODE_GENERATE_TESTS;
break;
default:
isa_usage(1, "\n");
break;
}
}
/*
* Create the canonical list of specification files to
* be processed.
*/
for (;optind < argc; optind++) {
if ((isa_opt = isa_make_option(argv[optind])) == NULL)
goto error;
SLIST_INSERT_HEAD(&config.isa_specs, isa_opt,
isa_next);
}
exit(0);
error:
err(1, "ERROR: Invocation failed");
}

49
ld/Makefile Normal file
View File

@ -0,0 +1,49 @@
# $Id: Makefile 2910 2013-02-03 06:06:23Z kaiwang27 $
TOP= ..
PROG= ld
WARNS?= 6
SRCS= amd64.c \
amd64_script.c \
i386.c \
i386_script.c \
ld_arch.c \
ld_dynamic.c \
ld_ehframe.c \
ld_error.c \
ld_exp.c \
ld_file.c \
ld_hash.c \
ld_input.c \
ld_layout.c \
ld_main.c \
ld_options.c \
ld_output.c \
ld_path.c \
ld_reloc.c \
ld_script.c \
ld_script_lexer.l \
ld_script_parser.y \
ld_strtab.c \
ld_symbols.c \
ld_symver.c \
y.tab.h
.SUFFIXES: .ld .c
.ld.c:
awk -f ld_script.awk ${.ALLSRC} > ${.TARGET}
GENSRCS= amd64_script.c i386_script.c
CLEANFILES+= y.tab.h ${GENSRCS}
DPADD= ${LIBELFTC} ${LIBELF} ${LIBDWARF}
LDADD= -lelftc -ldwarf -lelf
CFLAGS+= -I. -I${.CURDIR}
NOMAN=
.include "${TOP}/mk/elftoolchain.prog.mk"

1363
ld/amd64.c Normal file

File diff suppressed because it is too large Load Diff

31
ld/amd64.h Normal file
View File

@ -0,0 +1,31 @@
/*-
* Copyright (c) 2012 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: amd64.h 2500 2012-04-29 18:07:43Z kaiwang27 $
*/
char *amd64_script;
void amd64_register(struct ld *);

151
ld/amd64_script.ld Normal file
View File

@ -0,0 +1,151 @@
/* $Id: amd64_script.ld 2806 2012-12-24 08:23:59Z kaiwang27 $ */
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
ENTRY(_start)
SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib");
SECTIONS {
PROVIDE(__executable_start = 0x400000);
. = 0x400000 + SIZEOF_HEADERS;
.interp : { *(.interp) }
.hash : { *(.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rel.dyn :
{
*(.rel.init)
*(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
*(.rel.fini)
*(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
*(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
*(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
*(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
*(.rel.ctors)
*(.rel.dtors)
*(.rel.got)
*(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
}
.rela.dyn :
{
*(.rela.init)
*(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
*(.rela.fini)
*(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
*(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
*(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
*(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
*(.rela.ctors)
*(.rela.dtors)
*(.rela.got)
*(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
}
.rel.plt : { *(.rel.plt) }
.rela.plt : { *(.rela.plt) }
.init :
{
KEEP(*(.init))
} = 0x90909090
.plt : { *(.plt) }
.text :
{
*(.text .stub .text.* .gnu.linkonce.t.*)
} = 0x90909090
.fini :
{
KEEP(*(.fini))
} = 0x90909090
PROVIDE(__etext = .);
PROVIDE(_etext = .);
PROVIDE(etext = .);
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1 : { *(.rodata1) }
.eh_frame_hdr : { *(.eh_frame_hdr) }
.eh_frame : { KEEP(*(.eh_frame)) }
.gcc_except_table : { *(.gcc_except_table) }
. = ALIGN (CONSTANT(MAXPAGESIZE)) - ((CONSTANT(MAXPAGESIZE) - .) & (CONSTANT(MAXPAGESIZE) - 1));
. = DATA_SEGMENT_ALIGN (CONSTANT(MAXPAGESIZE), CONSTANT(COMMONPAGESIZE));
.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
PROVIDE(__preinit_array_start = .);
.preinit_array : { *(.preinit_array) }
PROVIDE(__preinit_array_end = .);
PROVIDE(__init_array_start = .);
.init_array : { *(.init_array) }
PROVIDE(__init_array_end = .);
PROVIDE(__fini_array_start = .);
.fini_array : { *(.fini_array) }
PROVIDE(__fini_array_end = .);
.ctors :
{
KEEP(*crtbegin*.o(.ctors))
KEEP(*(EXCLUDE_FILE (*crtend*.o ) .ctors))
KEEP(*(SORT(.ctors.*)))
KEEP(*(.ctors))
}
.dtors :
{
KEEP(*crtbegin*.o(.dtors))
KEEP(*(EXCLUDE_FILE (*crtend*.o ) .dtors))
KEEP(*(SORT(.dtors.*)))
KEEP(*(.dtors))
}
.jcr : { KEEP(*(.jcr)) }
.data.rel.ro : {
*(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*)
*(.data.rel.ro* .gnu.linkonce.d.rel.ro.*)
}
.dynamic : { *(.dynamic) }
.got : { *(.got) }
.got.plt : { *(.got.plt) }
.data :
{
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
_edata = .;
PROVIDE(edata = .);
__bss_start = .;
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
. = ALIGN(64 / 8);
}
. = ALIGN(64 / 8);
_end = .;
PROVIDE(end = .);
. = DATA_SEGMENT_END (.);
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF1 Extension */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/DISCARD/ : { *(.note.GNU-stack) }
}

624
ld/i386.c Normal file
View File

@ -0,0 +1,624 @@
/*-
* Copyright (c) 2012,2013 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "ld.h"
#include "ld_arch.h"
#include "ld_dynamic.h"
#include "ld_input.h"
#include "ld_output.h"
#include "ld_reloc.h"
#include "ld_symbols.h"
#include "ld_utils.h"
#include "i386.h"
ELFTC_VCSID("$Id: i386.c 2967 2013-10-12 23:58:13Z kaiwang27 $");
static void _create_plt_reloc(struct ld *ld, struct ld_symbol *lsb,
uint64_t offset);
static void _create_got_reloc(struct ld *ld, struct ld_symbol *lsb,
uint64_t type, uint64_t offset);
static void _create_copy_reloc(struct ld *ld, struct ld_symbol *lsb);
static void _create_dynamic_reloc(struct ld *ld, struct ld_input_section *is,
struct ld_symbol *lsb, uint64_t type, uint64_t offset);
static void _scan_reloc(struct ld *ld, struct ld_input_section *is,
struct ld_reloc_entry *lre);
static struct ld_input_section *_find_and_create_got_section(struct ld *ld,
int create);
static struct ld_input_section *_find_and_create_gotplt_section(struct ld *ld,
int create);
static struct ld_input_section *_find_and_create_plt_section(struct ld *ld,
int create);
static uint64_t _get_max_page_size(struct ld *ld);
static uint64_t _get_common_page_size(struct ld *ld);
static void _process_reloc(struct ld *ld, struct ld_input_section *is,
struct ld_reloc_entry *lre, struct ld_symbol *lsb, uint8_t *buf);
static const char *_reloc2str(uint64_t r);
static void _reserve_got_entry(struct ld *ld, struct ld_symbol *lsb, int num);
static void _reserve_gotplt_entry(struct ld *ld, struct ld_symbol *lsb);
static void _reserve_plt_entry(struct ld *ld, struct ld_symbol *lsb);
static int _is_absolute_reloc(uint64_t r);
static int _is_relative_reloc(uint64_t r);
static void _warn_pic(struct ld *ld, struct ld_reloc_entry *lre);
static uint32_t _got_offset(struct ld *ld, struct ld_symbol *lsb);
static uint64_t
_get_max_page_size(struct ld *ld)
{
(void) ld;
return (0x1000);
}
static uint64_t
_get_common_page_size(struct ld *ld)
{
(void) ld;
return (0x1000);
}
static const char *
_reloc2str(uint64_t r)
{
static char s[32];
switch (r) {
case 0: return "R_386_NONE";
case 1: return "R_386_32";
case 2: return "R_386_PC32";
case 3: return "R_386_GOT32";
case 4: return "R_386_PLT32";
case 5: return "R_386_COPY";
case 6: return "R_386_GLOB_DAT";
case 7: return "R_386_JMP_SLOT";
case 8: return "R_386_RELATIVE";
case 9: return "R_386_GOTOFF";
case 10: return "R_386_GOTPC";
case 14: return "R_386_TLS_TPOFF";
case 15: return "R_386_TLS_IE";
case 16: return "R_386_TLS_GOTI";
case 17: return "R_386_TLS_LE";
case 18: return "R_386_TLS_GD";
case 19: return "R_386_TLS_LDM";
case 24: return "R_386_TLS_GD_32";
case 25: return "R_386_TLS_GD_PUSH";
case 26: return "R_386_TLS_GD_CALL";
case 27: return "R_386_TLS_GD_POP";
case 28: return "R_386_TLS_LDM_32";
case 29: return "R_386_TLS_LDM_PUSH";
case 30: return "R_386_TLS_LDM_CALL";
case 31: return "R_386_TLS_LDM_POP";
case 32: return "R_386_TLS_LDO_32";
case 33: return "R_386_TLS_IE_32";
case 34: return "R_386_TLS_LE_32";
case 35: return "R_386_TLS_DTPMOD32";
case 36: return "R_386_TLS_DTPOFF32";
case 37: return "R_386_TLS_TPOFF32";
default:
snprintf(s, sizeof(s), "<unkown: %ju>", r);
return (s);
}
}
static int
_is_absolute_reloc(uint64_t r)
{
if (r == R_386_32)
return (1);
return (0);
}
static int
_is_relative_reloc(uint64_t r)
{
if (r == R_386_RELATIVE)
return (1);
return (0);
}
static void
_warn_pic(struct ld *ld, struct ld_reloc_entry *lre)
{
struct ld_symbol *lsb;
lsb = lre->lre_sym;
if (lsb->lsb_bind != STB_LOCAL)
ld_warn(ld, "relocation %s against `%s' can not be used"
" by runtime linker; recompile with -fPIC",
_reloc2str(lre->lre_type), lsb->lsb_name);
else
ld_warn(ld, "relocation %s can not be used by runtime linker;"
" recompile with -fPIC", _reloc2str(lre->lre_type));
}
static struct ld_input_section *
_find_and_create_got_section(struct ld *ld, int create)
{
struct ld_input_section *is;
/* Check if the GOT section is already created. */
is = ld_input_find_internal_section(ld, ".got");
if (is != NULL)
return (is);
if (create) {
is = ld_input_add_internal_section(ld, ".got");
is->is_entsize = 4;
is->is_align = 4;
is->is_type = SHT_PROGBITS;
is->is_flags = SHF_ALLOC | SHF_WRITE;
}
return (is);
}
static struct ld_input_section *
_find_and_create_gotplt_section(struct ld *ld, int create)
{
struct ld_input_section *is;
/* Check if the GOT (for PLT) section is already created. */
is = ld_input_find_internal_section(ld, ".got.plt");
if (is != NULL)
return (is);
if (create) {
is = ld_input_add_internal_section(ld, ".got.plt");
is->is_entsize = 4;
is->is_align = 4;
is->is_type = SHT_PROGBITS;
is->is_flags = SHF_ALLOC | SHF_WRITE;
/* Reserve space for the initial entries. */
(void) ld_input_reserve_ibuf(is, 3);
/* Create _GLOBAL_OFFSET_TABLE_ symbol. */
ld_symbols_add_internal(ld, "_GLOBAL_OFFSET_TABLE_", 0, 0,
is->is_index, STB_LOCAL, STT_OBJECT, STV_HIDDEN, is, NULL);
}
return (is);
}
static struct ld_input_section *
_find_and_create_plt_section(struct ld *ld, int create)
{
struct ld_input_section *is;
/* Check if the PLT section is already created. */
is = ld_input_find_internal_section(ld, ".plt");
if (is != NULL)
return (is);
if (create) {
is = ld_input_add_internal_section(ld, ".plt");
is->is_entsize = 4;
is->is_align = 4;
is->is_type = SHT_PROGBITS;
is->is_flags = SHF_ALLOC | SHF_EXECINSTR;
/* Reserve space for the initial entry. */
(void) ld_input_reserve_ibuf(is, 1);
}
return (is);
}
static void
_reserve_got_entry(struct ld *ld, struct ld_symbol *lsb, int num)
{
struct ld_input_section *is;
is = _find_and_create_got_section(ld, 1);
/* Check if the entry already has a GOT entry. */
if (lsb->lsb_got)
return;
/* Reserve GOT entries. */
lsb->lsb_got_off = ld_input_reserve_ibuf(is, num);
lsb->lsb_got = 1;
}
static void
_reserve_gotplt_entry(struct ld *ld, struct ld_symbol *lsb)
{
struct ld_input_section *is;
uint64_t off;
is = _find_and_create_gotplt_section(ld, 1);
/* Reserve a GOT entry for PLT. */
off = ld_input_reserve_ibuf(is, 1);
/*
* Record a R_386_JMP_SLOT entry for this symbol. Note that
* we don't need to record the offset (relative to the GOT section)
* here, since the PLT relocations will be sorted later and we
* will generate GOT section according to the new order.
*/
_create_plt_reloc(ld, lsb, 0);
}
static void
_reserve_plt_entry(struct ld *ld, struct ld_symbol *lsb)
{
struct ld_input_section *is;
is = _find_and_create_plt_section(ld, 1);
(void) ld_input_reserve_ibuf(is, 1);
lsb->lsb_plt = 1;
}
static void
_create_plt_reloc(struct ld *ld, struct ld_symbol *lsb, uint64_t offset)
{
ld_reloc_create_entry(ld, ".rel.plt", NULL, R_386_JMP_SLOT,
lsb, offset, 0);
lsb->lsb_dynrel = 1;
}
static void
_create_got_reloc(struct ld *ld, struct ld_symbol *lsb, uint64_t type,
uint64_t offset)
{
struct ld_input_section *tis;
tis = _find_and_create_got_section(ld, 0);
assert(tis != NULL);
ld_reloc_create_entry(ld, ".rel.got", tis, type, lsb, offset, 0);
if (type != R_386_RELATIVE)
lsb->lsb_dynrel = 1;
}
static void
_create_copy_reloc(struct ld *ld, struct ld_symbol *lsb)
{
struct ld_input_section *tis;
ld_dynamic_reserve_dynbss_entry(ld, lsb);
tis = ld_input_find_internal_section(ld, ".dynbss");
assert(tis != NULL);
ld_reloc_create_entry(ld, ".rel.bss", tis, R_386_COPY, lsb,
lsb->lsb_value, 0);
lsb->lsb_dynrel = 1;
}
static void
_create_dynamic_reloc(struct ld *ld, struct ld_input_section *is,
struct ld_symbol *lsb, uint64_t type, uint64_t offset)
{
if (lsb->lsb_bind == STB_LOCAL) {
if (is->is_flags & SHF_WRITE)
ld_reloc_create_entry(ld, ".rel.data.rel.local",
is, type, lsb, offset, 0);
else
ld_reloc_create_entry(ld, ".rel.data.rel.ro.local",
is, type, lsb, offset, 0);
} else {
if (is->is_flags & SHF_WRITE)
ld_reloc_create_entry(ld, ".rel.data.rel",
is, type, lsb, offset, 0);
else
ld_reloc_create_entry(ld, ".rel.data.rel.ro",
is, type, lsb, offset, 0);
}
if (type != R_386_RELATIVE)
lsb->lsb_dynrel = 1;
}
static void
_scan_reloc(struct ld *ld, struct ld_input_section *is,
struct ld_reloc_entry *lre)
{
struct ld_symbol *lsb;
lsb = ld_symbols_ref(lre->lre_sym);
switch (lre->lre_type) {
case R_386_NONE:
break;
case R_386_32:
/*
* For a local symbol, if te linker output a PIE or DSO,
* we should generate a R_386_RELATIVE reloc for R_386_32.
*/
if (lsb->lsb_bind == STB_LOCAL) {
if (ld->ld_pie || ld->ld_dso)
_create_dynamic_reloc(ld, is, lsb,
R_386_RELATIVE, lre->lre_offset);
break;
}
/*
* For a global symbol, we probably need to generate PLE entry
* and/ore a dynamic relocation.
*
* Note here, normally the compiler will generate a PC-relative
* relocation for function calls. However, if the code retrieve
* the address of a function and call it indirectly, assembler
* will generate absolute relocation instead. That's why we
* should check if we need to create a PLT entry here. Also, if
* we're going to create the PLT entry, we should also set the
* symbol value to the address of PLT entry just in case the
* function address is used to compare with other function
* addresses. (If PLT address is used, function will have
* unified address in the main executable and DSOs)
*/
if (ld_reloc_require_plt(ld, lre)) {
if (!lsb->lsb_plt) {
_reserve_gotplt_entry(ld, lsb);
_reserve_plt_entry(ld, lsb);
}
/*
* Note here even if we have generated PLT for this
* function before, we still need to set this flag.
* It's possible that we first see the relative
* relocation then this absolute relocation, in
* other words, the same function can be called in
* different ways.
*/
lsb->lsb_func_addr = 1;
}
if (ld_reloc_require_copy_reloc(ld, lre) &&
!lsb->lsb_copy_reloc)
_create_copy_reloc(ld, lsb);
else if (ld_reloc_require_dynamic_reloc(ld, lre)) {
/*
* Check if we can relax R_386_32 to
* R_386_RELATIVE instead.
*/
if (ld_reloc_relative_relax(ld, lre))
_create_dynamic_reloc(ld, is, lsb,
R_386_RELATIVE, lre->lre_offset);
else
_create_dynamic_reloc(ld, is, lsb,
R_386_32, lre->lre_offset);
}
break;
case R_386_PLT32:
/*
* In some cases we don't really need to generate a PLT
* entry, then a R_386_PLT32 relocation can be relaxed
* to a R_386_PC32 relocation.
*/
if (lsb->lsb_bind == STB_LOCAL ||
!ld_reloc_require_plt(ld, lre)) {
lre->lre_type = R_386_PC32;
break;
}
/*
* If linker outputs an normal executable and the symbol is
* defined but is not defined inside a DSO, we can generate
* a R_386_PC32 relocation instead.
*/
if (ld->ld_exec && lsb->lsb_shndx != SHN_UNDEF &&
(lsb->lsb_input == NULL ||
lsb->lsb_input->li_type != LIT_DSO)) {
lre->lre_type = R_386_PC32;
break;
}
/* Create an PLT entry otherwise. */
if (!lsb->lsb_plt) {
_reserve_gotplt_entry(ld, lsb);
_reserve_plt_entry(ld, lsb);
}
break;
case R_386_PC32:
/*
* When R_386_PC32 apply to a global symbol, we should
* check if we need to generate PLT entry and/or a dynamic
* relocation.
*/
if (lsb->lsb_bind != STB_LOCAL) {
if (ld_reloc_require_plt(ld, lre) && !lsb->lsb_plt) {
_reserve_gotplt_entry(ld, lsb);
_reserve_plt_entry(ld, lsb);
}
if (ld_reloc_require_copy_reloc(ld, lre) &&
!lsb->lsb_copy_reloc)
_create_copy_reloc(ld, lsb);
else if (ld_reloc_require_dynamic_reloc(ld, lre)) {
/*
* We can not generate dynamic relocation for
* these PC-relative relocation since they
* are probably not supported by the runtime
* linkers.
*/
_warn_pic(ld, lre);
}
}
break;
case R_386_GOTOFF:
case R_386_GOTPC:
/*
* These relocation types use GOT address as a base address
* and instruct the linker to build a GOT.
*/
(void) _find_and_create_got_section(ld, 1);
break;
case R_386_GOT32:
/*
* R_386_GOT32 relocation instructs the linker to build a
* GOT and generate a GOT entry.
*/
if (!lsb->lsb_got) {
_reserve_got_entry(ld, lsb, 1);
/*
* TODO: For now we always create a R_386_GLOB_DAT
* relocation for a GOT entry. There are cases that
* the symbol's address is known at link time and
* the GOT entry value can be filled in by the program
* linker instead.
*/
if (ld_reloc_require_glob_dat(ld, lre))
_create_got_reloc(ld, lsb, R_386_GLOB_DAT,
lsb->lsb_got_off);
else
_create_got_reloc(ld, lsb, R_386_RELATIVE,
lsb->lsb_got_off);
}
default:
ld_warn(ld, "can not handle relocation %ju",
lre->lre_type);
break;
}
}
static uint32_t
_got_offset(struct ld *ld, struct ld_symbol *lsb)
{
struct ld_output_section *os;
assert(lsb->lsb_got);
if (ld->ld_got == NULL) {
ld->ld_got = _find_and_create_got_section(ld, 0);
assert(ld->ld_got != NULL);
}
os = ld->ld_got->is_output;
return (os->os_addr + ld->ld_got->is_reloff + lsb->lsb_got_off);
}
static void
_process_reloc(struct ld *ld, struct ld_input_section *is,
struct ld_reloc_entry *lre, struct ld_symbol *lsb, uint8_t *buf)
{
struct ld_state *ls;
struct ld_output *lo;
uint32_t p, s, l, g, got;
int32_t a, v;
ls = &ld->ld_state;
lo = ld->ld_output;
assert(lo != NULL);
l = lsb->lsb_plt_off;
p = lre->lre_offset + is->is_output->os_addr + is->is_reloff;
got = ld->ld_got->is_output->os_addr;
s = (uint32_t) lsb->lsb_value;
READ_32(buf + lre->lre_offset, a);
switch (lre->lre_type) {
case R_386_NONE:
break;
case R_386_32:
v = s + a;
WRITE_32(buf + lre->lre_offset, v);
break;
case R_386_PC32:
if (lsb->lsb_plt)
v = l + a - p;
else
v = s + a - p;
WRITE_32(buf + lre->lre_offset, v);
break;
case R_386_PLT32:
if (!ls->ls_ignore_next_plt) {
v = l + a - p;
WRITE_32(buf + lre->lre_offset, v);
} else
ls->ls_ignore_next_plt = 0;
break;
case R_386_GOT32:
g = _got_offset(ld, lsb);
v = g + a;
WRITE_32(buf + lre->lre_offset, v);
break;
case R_386_GOTOFF:
v = s + a - got;
WRITE_32(buf + lre->lre_offset, v);
break;
case R_386_GOTPC:
v = got + a - p;
WRITE_32(buf + lre->lre_offset, v);
break;
default:
ld_fatal(ld, "Relocation %d not supported", lre->lre_type);
break;
}
}
void
i386_register(struct ld *ld)
{
struct ld_arch *i386_arch;
if ((i386_arch = calloc(1, sizeof(*i386_arch))) == NULL)
ld_fatal_std(ld, "calloc");
snprintf(i386_arch->name, sizeof(i386_arch->name), "%s", "i386");
i386_arch->script = i386_script;
i386_arch->get_max_page_size = _get_max_page_size;
i386_arch->get_common_page_size = _get_common_page_size;
i386_arch->scan_reloc = _scan_reloc;
i386_arch->process_reloc = _process_reloc;
i386_arch->is_absolute_reloc = _is_absolute_reloc;
i386_arch->is_relative_reloc = _is_relative_reloc;
i386_arch->reloc_is_64bit = 0;
i386_arch->reloc_is_rela = 0;
i386_arch->reloc_entsize = sizeof(Elf32_Rel);
HASH_ADD_STR(ld->ld_arch_list, name, i386_arch);
}

31
ld/i386.h Normal file
View File

@ -0,0 +1,31 @@
/*-
* Copyright (c) 2012 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: i386.h 2500 2012-04-29 18:07:43Z kaiwang27 $
*/
char *i386_script;
void i386_register(struct ld *);

148
ld/i386_script.ld Normal file
View File

@ -0,0 +1,148 @@
/* $Id: i386_script.ld 2664 2012-11-04 08:39:22Z kaiwang27 $ */
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)
SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib");
SECTIONS {
PROVIDE (__executable_start = 0x08048000);
. = 0x08048000 + SIZEOF_HEADERS;
.interp : { *(.interp) }
.hash : { *(.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rel.dyn :
{
*(.rel.init)
*(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
*(.rel.fini)
*(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
*(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
*(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
*(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
*(.rel.ctors)
*(.rel.dtors)
*(.rel.got)
*(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
}
.rela.dyn :
{
*(.rela.init)
*(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
*(.rela.fini)
*(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
*(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
*(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
*(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
*(.rela.ctors)
*(.rela.dtors)
*(.rela.got)
*(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
}
.rel.plt : { *(.rel.plt) }
.rela.plt : { *(.rela.plt) }
.init :
{
KEEP(*(.init))
} = 0x90909090
.plt : { *(.plt) }
.text :
{
*(.text .stub .text.* .gnu.linkonce.t.*)
} = 0x90909090
.fini :
{
KEEP(*(.fini))
} = 0x90909090
PROVIDE(__etext = .);
PROVIDE(_etext = .);
PROVIDE(etext = .);
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1 : { *(.rodata1) }
.eh_frame_hdr : { *(.eh_frame_hdr) }
.eh_frame : { KEEP(*(.eh_frame)) }
.gcc_except_table : { *(.gcc_except_table) }
. = ALIGN (CONSTANT(COMMONPAGESIZE)) - ((CONSTANT(COMMONPAGESIZE) - .) & (CONSTANT(COMMONPAGESIZE) - 1));
. = DATA_SEGMENT_ALIGN (CONSTANT(COMMONPAGESIZE), CONSTANT(COMMONPAGESIZE));
. = ALIGN(32 / 8);
.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
PROVIDE(__preinit_array_start = .);
.preinit_array : { *(.preinit_array) }
PROVIDE(__preinit_array_end = .);
PROVIDE(__init_array_start = .);
.init_array : { *(.init_array) }
PROVIDE(__init_array_end = .);
PROVIDE(__fini_array_start = .);
.fini_array : { *(.fini_array) }
PROVIDE(__fini_array_end = .);
.dynamic : { *(.dynamic) }
.ctors :
{
KEEP(*crtbegin*.o(.ctors))
KEEP(*(EXCLUDE_FILE (*crtend*.o ) .ctors))
KEEP(*(SORT(.ctors.*)))
KEEP(*(.ctors))
}
.dtors :
{
KEEP(*crtbegin*.o(.dtors))
KEEP(*(EXCLUDE_FILE (*crtend*.o ) .dtors))
KEEP(*(SORT(.dtors.*)))
KEEP(*(.dtors))
}
.jcr : { KEEP(*(.jcr)) }
.got : { *(.got.plt) *(.got) }
.data :
{
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
_edata = .;
PROVIDE(edata = .);
__bss_start = .;
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
. = ALIGN(32 / 8);
}
. = ALIGN(32 / 8);
_end = .;
PROVIDE(end = .);
. = DATA_SEGMENT_END (.);
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF1 Extension */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/DISCARD/ : { *(.note.GNU-stack) }
}

158
ld/ld.h Normal file
View File

@ -0,0 +1,158 @@
/*-
* Copyright (c) 2010-2013 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: ld.h 2940 2013-05-04 22:22:10Z kaiwang27 $
*/
#include <sys/cdefs.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <ar.h>
#include <assert.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <gelf.h>
#include <inttypes.h>
#include <libelftc.h>
#include <libgen.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "dwarf.h"
#define oom() ld_fatal(ld, "out of memory")
#include "utarray.h"
#define uthash_fatal(msg) ld_fatal(ld, msg)
#include "uthash.h"
#include "_elftc.h"
struct ld_file;
struct ld_input_section_head;
struct ld_path;
struct ld_symbol;
struct ld_symbol_head;
struct ld_output_data_buffer;
struct ld_wildcard_match;
struct ld_ehframe_cie_head;
struct ld_ehframe_fde_head;
struct ld_section_group;
#define LD_MAX_NESTED_GROUP 16
struct ld_state {
Elftc_Bfd_Target *ls_itgt; /* input bfd target set by -b */
struct ld_file *ls_file; /* current open file */
unsigned ls_static; /* use static library */
unsigned ls_whole_archive; /* include whole archive */
unsigned ls_as_needed; /* DT_NEEDED */
unsigned ls_group_level; /* archive group level */
unsigned ls_extracted[LD_MAX_NESTED_GROUP + 1];
/* extracted from archive group */
unsigned ls_search_dir; /* search library directories */
uint64_t ls_loc_counter; /* location counter */
uint64_t ls_offset; /* cur. output section file offset */
STAILQ_HEAD(, ld_path) ls_lplist; /* search path list */
STAILQ_HEAD(, ld_path) ls_rplist; /* rpath list */
STAILQ_HEAD(, ld_path) ls_rllist; /* rpath-link list */
unsigned ls_arch_conflict; /* input arch conflict with output */
unsigned ls_first_elf_object; /* first ELF object to process */
unsigned ls_rerun; /* ld(1) restarted */
unsigned ls_archive_mb_header; /* extracted list header printed */
unsigned ls_first_output_sec; /* flag indicates 1st output section */
unsigned ls_ignore_next_plt; /* ignore next PLT relocation */
unsigned ls_version_local; /* version entry is local */
uint64_t ls_relative_reloc; /* number of *_RELATIVE relocations */
struct ld_input_section_head *ls_gc;
/* garbage collection search list */
};
struct ld {
const char *ld_progname; /* ld(1) program name */
struct ld_arch *ld_arch; /* arch-specific callbacks */
struct ld_arch *ld_arch_list; /* list of supported archs */
Elftc_Bfd_Target *ld_otgt; /* default output format */
Elftc_Bfd_Target *ld_otgt_be; /* big-endian output format */
Elftc_Bfd_Target *ld_otgt_le; /* little-endian output format */
char *ld_otgt_name; /* output format name */
char *ld_otgt_be_name; /* big-endian output format name */
char *ld_otgt_le_name; /* little-endian output format name */
struct ld_output *ld_output; /* output object */
char *ld_output_file; /* output file name */
char *ld_entry; /* entry point set by -e */
char *ld_scp_entry; /* entry point set by linker script */
char *ld_interp; /* dynamic linker */
char *ld_soname; /* DT_SONAME */
struct ld_script *ld_scp; /* linker script */
struct ld_state ld_state; /* linker state */
struct ld_strtab *ld_shstrtab; /* section name table */
struct ld_symbol_head *ld_ext_symbols; /* -u/EXTERN symbols */
struct ld_symbol_head *ld_var_symbols; /* ldscript var symbols */
struct ld_symbol *ld_sym; /* internal symbol table */
struct ld_symbol *ld_symtab_import; /* hash for import symbols */
struct ld_symbol *ld_symtab_export; /* hash for export symbols */
struct ld_symbol_defver *ld_defver; /* default version table */
struct ld_symbol_table *ld_symtab; /* .symtab symbol table */
struct ld_strtab *ld_strtab; /* .strtab string table */
struct ld_symbol_table *ld_dynsym; /* .dynsym symbol table */
struct ld_strtab *ld_dynstr; /* .dynstr string table */
struct ld_symbol_head *ld_dyn_symbols; /* dynamic symbol list */
struct ld_wildcard_match *ld_wm; /* wildcard hash table */
struct ld_input_section *ld_dynbss; /* .dynbss section */
struct ld_input_section *ld_got; /* .got section */
struct ld_ehframe_cie_head *ld_cie; /* ehframe CIE list */
struct ld_ehframe_fde_head *ld_fde; /* ehframe FDE list */
struct ld_section_group *ld_sg; /* included section groups */
unsigned char ld_common_alloc; /* always alloc space for common sym */
unsigned char ld_common_no_alloc; /* never alloc space for common sym */
unsigned char ld_emit_reloc; /* emit relocations */
unsigned char ld_gen_gnustack; /* generate PT_GNUSTACK */
unsigned char ld_print_linkmap; /* print link map */
unsigned char ld_stack_exec; /* stack executable */
unsigned char ld_stack_exec_set; /* stack executable override */
unsigned char ld_exec; /* output normal executable */
unsigned char ld_pie; /* position-independent executable */
unsigned char ld_dso; /* output shared library */
unsigned char ld_reloc; /* output relocatable object */
unsigned char ld_dynamic_link; /* perform dynamic linking */
unsigned char ld_print_version; /* linker version printed */
unsigned char ld_gc; /* perform garbage collection */
unsigned char ld_gc_print; /* print removed sections */
unsigned char ld_ehframe_hdr; /* create .eh_frame_hdr section */
STAILQ_HEAD(ld_input_head, ld_input) ld_lilist; /* input object list */
TAILQ_HEAD(ld_file_head, ld_file) ld_lflist; /* input file list */
};
void ld_err(struct ld *, const char *, ...);
void ld_fatal(struct ld *, const char *, ...);
void ld_fatal_std(struct ld *, const char *, ...);
void ld_warn(struct ld *, const char *, ...);
void ld_info(struct ld *, const char *, ...);

209
ld/ld_arch.c Normal file
View File

@ -0,0 +1,209 @@
/*-
* Copyright (c) 2011,2012 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "ld.h"
#include "ld_arch.h"
#include "i386.h"
#include "amd64.h"
ELFTC_VCSID("$Id: ld_arch.c 2515 2012-06-06 23:05:00Z kaiwang27 $");
#define LD_DEFAULT_ARCH "amd64"
static struct ld_arch *_get_arch_from_target(struct ld *ld, char *target);
void
ld_arch_init(struct ld *ld)
{
char *end;
char arch[MAX_ARCH_NAME_LEN + 1], target[MAX_TARGET_NAME_LEN + 1];
size_t len;
/*
* Register supported architectures.
*/
i386_register(ld);
amd64_register(ld);
/*
* Find out default arch for output object.
*/
if ((end = strrchr(ld->ld_progname, '-')) != NULL &&
end != ld->ld_progname) {
len = end - ld->ld_progname + 1;
if (len > MAX_TARGET_NAME_LEN)
return;
strncpy(target, ld->ld_progname, len);
target[len] = '\0';
ld->ld_arch = _get_arch_from_target(ld, target);
}
if (ld->ld_arch == NULL) {
snprintf(arch, sizeof(arch), "%s", LD_DEFAULT_ARCH);
ld->ld_arch = ld_arch_find(ld, arch);
if (ld->ld_arch == NULL)
ld_fatal(ld, "Internal: could not determine output"
" object architecture");
}
}
void
ld_arch_set(struct ld *ld, char *arch)
{
ld->ld_arch = ld_arch_find(ld, arch);
if (ld->ld_arch == NULL)
ld_fatal(ld, "arch '%s' is not supported", arch);
}
void
ld_arch_set_from_target(struct ld *ld)
{
if (ld->ld_otgt != NULL) {
ld->ld_arch = _get_arch_from_target(ld, ld->ld_otgt_name);
if (ld->ld_arch == NULL)
ld_fatal(ld, "target '%s' is not supported",
ld->ld_otgt_name);
}
}
int
ld_arch_equal(struct ld_arch *a1, struct ld_arch *a2)
{
assert(a1 != NULL && a2 != NULL);
if (a1 == a2)
return (1);
if (a1->alias == a2 || a2->alias == a1)
return (1);
if (a1->alias != NULL && a1->alias == a2->alias)
return (1);
return (0);
}
void
ld_arch_verify(struct ld *ld, const char *name, int mach)
{
struct ld_arch *la;
struct ld_state *ls;
assert(ld->ld_arch != NULL);
ls = &ld->ld_state;
if ((la = ld_arch_guess_arch_name(ld, mach)) == NULL)
ld_fatal(ld, "%s: ELF object architecture %#x not supported",
name, mach);
if (!ld_arch_equal(la, ld->ld_arch)) {
ls->ls_arch_conflict = 1;
if (ls->ls_rerun || !ls->ls_first_elf_object)
ld_fatal(ld, "%s: ELF object architecture `%s' "
"conflicts with output object architecture `%s'",
name, la->name, ld->ld_arch->name);
ld->ld_arch = la;
}
ls->ls_first_elf_object = 0;
}
struct ld_arch *
ld_arch_guess_arch_name(struct ld *ld, int mach)
{
char arch[MAX_ARCH_NAME_LEN + 1];
/* TODO: we should also consider elf class and endianess. */
switch (mach) {
case EM_386:
snprintf(arch, sizeof(arch), "%s", "i386");
break;
case EM_ARM:
snprintf(arch, sizeof(arch), "%s", "arm");
break;
case EM_MIPS:
case EM_MIPS_RS3_LE:
snprintf(arch, sizeof(arch), "%s", "mips");
break;
case EM_PPC:
case EM_PPC64:
snprintf(arch, sizeof(arch), "%s", "ppc");
break;
case EM_SPARC:
case EM_SPARCV9:
snprintf(arch, sizeof(arch), "%s", "sparc");
break;
case EM_X86_64:
snprintf(arch, sizeof(arch), "%s", "amd64");
break;
default:
return (NULL);
}
return (ld_arch_find(ld, arch));
}
static struct ld_arch *
_get_arch_from_target(struct ld *ld, char *target)
{
struct ld_arch *la;
char *begin, *end, name[MAX_TARGET_NAME_LEN + 1];
if ((begin = strchr(target, '-')) == NULL) {
la = ld_arch_find(ld, target);
return (la);
}
la = ld_arch_find(ld, begin + 1);
if (la != NULL)
return (la);
strncpy(name, begin + 1, sizeof(name) - 1);
name[sizeof(name) - 1] = '\0';
while ((end = strrchr(name, '-')) != NULL) {
*end = '\0';
la = ld_arch_find(ld, name);
if (la != NULL)
return (la);
}
return (NULL);
}
struct ld_arch *
ld_arch_find(struct ld *ld, char *arch)
{
struct ld_arch *la;
HASH_FIND_STR(ld->ld_arch_list, arch, la);
return (la);
}

65
ld/ld_arch.h Normal file
View File

@ -0,0 +1,65 @@
/*-
* Copyright (c) 2011,2012 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: ld_arch.h 2913 2013-02-16 07:15:24Z kaiwang27 $
*/
#define MAX_ARCH_NAME_LEN 64
#define MAX_TARGET_NAME_LEN 128
struct ld_input_section;
struct ld_reloc_entry;
struct ld_arch {
char name[MAX_ARCH_NAME_LEN + 1];
char *script;
const char *interp;
uint64_t (*get_max_page_size)(struct ld *);
uint64_t (*get_common_page_size)(struct ld *);
void (*scan_reloc)(struct ld *, struct ld_input_section *,
struct ld_reloc_entry *);
void (*adjust_reloc)(struct ld *, struct ld_input_section *,
struct ld_reloc_entry *, struct ld_symbol *, uint8_t *);
void (*process_reloc)(struct ld *, struct ld_input_section *,
struct ld_reloc_entry *, struct ld_symbol *, uint8_t *);
void (*finalize_reloc)(struct ld *, struct ld_input_section *,
struct ld_reloc_entry *);
void (*finalize_got_and_plt)(struct ld *);
int (*is_absolute_reloc)(uint64_t);
int (*is_relative_reloc)(uint64_t);
unsigned char reloc_is_64bit;
unsigned char reloc_is_rela;
size_t reloc_entsize;
UT_hash_handle hh;
struct ld_arch *alias;
};
void ld_arch_init(struct ld *);
int ld_arch_equal(struct ld_arch *, struct ld_arch *);
struct ld_arch *ld_arch_find(struct ld *, char *);
struct ld_arch *ld_arch_guess_arch_name(struct ld *, int);
void ld_arch_set(struct ld *, char *);
void ld_arch_set_from_target(struct ld *);
void ld_arch_verify(struct ld *, const char *, int);

610
ld/ld_dynamic.c Normal file
View File

@ -0,0 +1,610 @@
/*-
* Copyright (c) 2012,2013 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "ld.h"
#include "ld_arch.h"
#include "ld_dynamic.h"
#include "ld_file.h"
#include "ld_hash.h"
#include "ld_input.h"
#include "ld_layout.h"
#include "ld_output.h"
#include "ld_path.h"
#include "ld_symbols.h"
#include "ld_symver.h"
#include "ld_strtab.h"
ELFTC_VCSID("$Id: ld_dynamic.c 2965 2013-09-10 02:46:29Z kaiwang27 $");
static void _check_dso_needed(struct ld *ld, struct ld_output *lo);
static void _create_dynamic(struct ld *ld, struct ld_output *lo);
static void _create_interp(struct ld *ld, struct ld_output *lo);
static void _create_dynsym_and_dynstr_section(struct ld *ld,
struct ld_output *lo);
static void _finalize_dynamic(struct ld *ld, struct ld_output *lo);
void
ld_dynamic_create(struct ld *ld)
{
struct ld_output *lo;
lo = ld->ld_output;
assert(lo != NULL);
/* Check how many DSOs is needed for output object. */
_check_dso_needed(ld, lo);
/* Link statically if we don't use DSOs? */
if (lo->lo_dso_needed == 0)
return;
ld->ld_dynamic_link = 1;
/* Create .interp section. */
if (!ld->ld_dso)
_create_interp(ld, lo);
/* Create .dynamic section. */
_create_dynamic(ld, lo);
/* Create .dynsym and .dynstr sections. */
_create_dynsym_and_dynstr_section(ld, lo);
/* Create .hash section. */
ld_hash_create_svr4_hash_section(ld);
/*
* Create .gnu.version_d section if the linker creats a shared
* library and version script is provided.
*/
lo->lo_version_index = 2;
if (ld->ld_dso)
ld_symver_create_verdef_section(ld);
/* Create .gnu.version_r section. */
ld_symver_create_verneed_section(ld);
/* Create .gnu.version section. */
ld_symver_create_versym_section(ld);
}
void
ld_dynamic_finalize(struct ld *ld)
{
struct ld_output *lo;
lo = ld->ld_output;
assert(lo != NULL);
if (lo->lo_dso_needed == 0)
return;
/* Finalize .dynamic section */
_finalize_dynamic(ld, lo);
}
void
ld_dynamic_load_dso_dynamic(struct ld *ld, struct ld_input *li, Elf *e,
Elf_Scn *scn, size_t strndx)
{
GElf_Shdr shdr;
GElf_Dyn dyn;
Elf_Data *d;
int elferr, i, len;
const char *name;
if (strndx == SHN_UNDEF)
return;
if (gelf_getshdr(scn, &shdr) != &shdr) {
ld_warn(ld, "%s: gelf_getshdr failed: %s", li->li_name,
elf_errmsg(-1));
return;
}
(void) elf_errno();
if ((d = elf_getdata(scn, NULL)) == NULL) {
elferr = elf_errno();
if (elferr != 0)
ld_warn(ld, "%s: elf_getdata failed: %s", li->li_name,
elf_errmsg(elferr));
return;
}
len = d->d_size / shdr.sh_entsize;
for (i = 0; i < len; i++) {
if (gelf_getdyn(d, i, &dyn) != &dyn) {
ld_warn(ld, "%s: gelf_getdyn failed: %s", li->li_name,
elf_errmsg(-1));
continue;
}
switch (dyn.d_tag) {
case DT_SONAME:
name = elf_strptr(e, strndx, dyn.d_un.d_ptr);
if (name != NULL &&
(li->li_soname = strdup(name)) == NULL)
ld_fatal_std(ld, "strdup");
break;
case DT_NEEDED:
name = elf_strptr(e, strndx, dyn.d_un.d_ptr);
if (name != NULL)
ld_path_search_dso_needed(ld, li->li_file,
name);
break;
default:
break;
}
}
}
void
ld_dynamic_reserve_dynbss_entry(struct ld *ld, struct ld_symbol *lsb)
{
struct ld_input *li;
struct ld_input_section *dynbss, *is;
uint64_t a;
/* Create .dynbss section if it doesn't yet exist. */
dynbss = ld_input_find_internal_section(ld, ".dynbss");
if (dynbss == NULL) {
dynbss = ld_input_add_internal_section(ld, ".dynbss");
dynbss->is_type = SHT_NOBITS;
}
li = lsb->lsb_input;
assert(li != NULL && li->li_type == LIT_DSO);
/*
* TODO: we don't have to create copy relocation
* for every import object. Some import objects
* are read-only, in that case we can create other
* dynamic relocations for them.
*/
/*
* If the section to which the symbols belong has a larger
* alignment requirement, we increase .dynbss section alignment
* accordingly. XXX What if it is a DSO common symbol?
*/
is = NULL;
if (lsb->lsb_shndx != SHN_COMMON) {
assert(lsb->lsb_shndx < li->li_shnum - 1);
is = &li->li_is[lsb->lsb_shndx];
if (is->is_align > dynbss->is_align)
dynbss->is_align = is->is_align;
}
/*
* Calculate the alignment for this object.
*/
if (is != NULL) {
for (a = is->is_align; a > 1; a >>= 1) {
if ((lsb->lsb_value - is->is_off) % a == 0)
break;
}
} else
a = 1;
if (a > 1)
dynbss->is_size = roundup(dynbss->is_size, a);
lsb->lsb_value = dynbss->is_size;
lsb->lsb_copy_reloc = 1;
lsb->lsb_input = dynbss->is_input;
lsb->lsb_shndx = dynbss->is_index;
lsb->lsb_is = dynbss;
dynbss->is_size += lsb->lsb_size;
}
static void
_create_interp(struct ld *ld, struct ld_output *lo)
{
struct ld_output_section *os;
struct ld_output_data_buffer *odb;
const char *interp;
char interp_name[] = ".interp";
HASH_FIND_STR(lo->lo_ostbl, interp_name, os);
if (os == NULL)
os = ld_layout_insert_output_section(ld, interp_name,
SHF_ALLOC);
os->os_type = SHT_PROGBITS;
os->os_align = 1;
os->os_entsize = 0;
os->os_flags = SHF_ALLOC;
lo->lo_interp = os;
if (ld->ld_interp != NULL)
interp = ld->ld_interp;
else
interp = ld->ld_arch->interp;
assert(interp != NULL);
if ((odb = calloc(1, sizeof(*odb))) == NULL)
ld_fatal_std(ld, "calloc");
odb->odb_size = strlen(interp) + 1;
odb->odb_align = 1;
odb->odb_type = ELF_T_BYTE;
if ((odb->odb_buf = calloc(odb->odb_size, 1)) == NULL)
ld_fatal_std(ld, "calloc");
strncpy(odb->odb_buf, interp, strlen(interp));
odb->odb_buf[strlen(interp)] = '\0';
(void) ld_output_create_section_element(ld, os, OET_DATA_BUFFER, odb,
NULL);
}
static void
_create_dynamic(struct ld *ld, struct ld_output *lo)
{
struct ld_output_section *os, *_os;
struct ld_output_data_buffer *odb;
char dynamic_name[] = ".dynamic";
char init_name[] = ".init";
char fini_name[] = ".fini";
char *rpath;
int entries;
HASH_FIND_STR(lo->lo_ostbl, dynamic_name, os);
if (os == NULL)
os = ld_layout_insert_output_section(ld, dynamic_name,
SHF_ALLOC | SHF_WRITE);
os->os_type = SHT_DYNAMIC;
os->os_flags = SHF_ALLOC | SHF_WRITE;
if (lo->lo_ec == ELFCLASS32) {
os->os_entsize = 8;
os->os_align = 4;
} else {
os->os_entsize = 16;
os->os_align = 8;
}
lo->lo_dynamic = os;
/* .dynamic section should link to .dynstr section. */
if ((os->os_link = strdup(".dynstr")) == NULL)
ld_fatal_std(ld, "strdup");
/* DT_NEEDED */
entries = lo->lo_dso_needed;
/* DT_SONAME. */
if (ld->ld_soname != NULL) {
lo->lo_soname_nameindex = ld_strtab_insert_no_suffix(ld,
ld->ld_dynstr, ld->ld_soname);
entries++;
}
/* DT_INIT */
HASH_FIND_STR(lo->lo_ostbl, init_name, _os);
if (_os != NULL && !_os->os_empty) {
lo->lo_init = _os;
entries++;
}
/* DT_FINI */
HASH_FIND_STR(lo->lo_ostbl, fini_name, _os);
if (_os != NULL && !_os->os_empty) {
lo->lo_fini = _os;
entries++;
}
/* DT_HASH, DT_STRTAB, DT_SYMTAB, DT_STRSZ and DT_SYMENT */
if (ld->ld_dynsym)
entries += 5;
/* DT_RPATH. */
if (!STAILQ_EMPTY(&ld->ld_state.ls_rplist)) {
rpath = ld_path_join_rpath(ld);
lo->lo_rpath_nameindex = ld_strtab_insert_no_suffix(ld,
ld->ld_dynstr, rpath);
entries++;
}
/*
* DT_DEBUG. dynamic linker changes this at runtime, gdb uses
* it to find all the loaded DSOs. (thus .dynamic has to be
* writable)
*/
if (!ld->ld_dso)
entries++;
/* DT_PLTGOT, DT_PLTRELSZ, DT_PLTREL and DT_JMPREL. */
entries += 4;
/* DT_REL/DT_RELA, DT_RELSZ/DT_RELASZ and DT_RELENT/DT_RELAENT */
entries += 3;
/*
* DT_VERNEED, DT_VERNEEDNUM, DT_VERDEF, DT_VERDEFNUM and DT_VERSYM.
*/
entries += 5;
/* DT_RELCOUNT/DT_RELACOUNT. */
if (ld->ld_state.ls_relative_reloc > 0)
entries++;
/* DT_NULL. TODO: Reserve multiple DT_NULL entries for DT_RPATH? */
entries++;
/*
* Reserve space for .dynamic section, based on number of entries.
*/
if ((odb = calloc(1, sizeof(*odb))) == NULL)
ld_fatal_std(ld, "calloc");
odb->odb_size = entries * os->os_entsize;
if ((odb->odb_buf = malloc(odb->odb_size)) == NULL)
ld_fatal_std(ld, "malloc");
odb->odb_align = os->os_align;
odb->odb_type = ELF_T_DYN;
(void) ld_output_create_section_element(ld, os, OET_DATA_BUFFER, odb,
NULL);
lo->lo_dynamic_odb = odb;
/* Create _DYNAMIC symobl. */
ld_symbols_add_internal(ld, "_DYNAMIC", 0, 0, SHN_ABS, STB_LOCAL,
STT_OBJECT, STV_HIDDEN, NULL, os);
}
#define DT_ENTRY_VAL(tag,val) \
do { \
if (lo->lo_ec == ELFCLASS32) { \
assert(dt32 < end32); \
dt32->d_tag = (int32_t) (tag); \
dt32->d_un.d_val = (uint32_t) (val); \
dt32++; \
} else { \
assert(dt64 < end64); \
dt64->d_tag = (tag); \
dt64->d_un.d_val = (val); \
dt64++; \
} \
} while(0)
#define DT_ENTRY_PTR(tag,ptr) \
do { \
if (lo->lo_ec == ELFCLASS32) { \
assert(dt32 < end32); \
dt32->d_tag = (int32_t) (tag); \
dt32->d_un.d_ptr = (uint32_t) (ptr); \
dt32++; \
} else { \
assert(dt64 < end64); \
dt64->d_tag = (tag); \
dt64->d_un.d_ptr = (ptr); \
dt64++; \
} \
} while(0)
#define DT_ENTRY_NULL \
do { \
if (lo->lo_ec == ELFCLASS32) { \
assert(dt32 < end32); \
while (dt32 < end32) \
DT_ENTRY_VAL(DT_NULL, 0); \
assert(dt32 == end32); \
} else { \
assert(dt64 < end64); \
while (dt64 < end64) \
DT_ENTRY_VAL(DT_NULL, 0); \
assert(dt64 == end64); \
} \
} while(0)
static void
_finalize_dynamic(struct ld *ld, struct ld_output *lo)
{
struct ld_output_data_buffer *odb;
Elf32_Dyn *dt32, *end32;
Elf64_Dyn *dt64, *end64;
int *p;
odb = lo->lo_dynamic_odb;
assert(odb != NULL);
dt32 = (Elf32_Dyn *) (uintptr_t) odb->odb_buf;
dt64 = (Elf64_Dyn *) (uintptr_t) odb->odb_buf;
end32 = (Elf32_Dyn *) (uintptr_t) (odb->odb_buf + odb->odb_size);
end64 = (Elf64_Dyn *) (uintptr_t) (odb->odb_buf + odb->odb_size);
/* DT_NEEDED. */
for (p = (int *) (uintptr_t) utarray_front(lo->lo_dso_nameindex);
p != NULL;
p = (int *) (uintptr_t) utarray_next(lo->lo_dso_nameindex, p))
DT_ENTRY_VAL(DT_NEEDED, *p);
/* DT_SONAME. */
if (ld->ld_soname != NULL)
DT_ENTRY_VAL(DT_SONAME, lo->lo_soname_nameindex);
/* DT_INIT and DT_FINI */
if (lo->lo_init != NULL)
DT_ENTRY_PTR(DT_INIT, lo->lo_init->os_addr);
if (lo->lo_fini != NULL)
DT_ENTRY_PTR(DT_FINI, lo->lo_fini->os_addr);
/* DT_HASH */
if (lo->lo_hash != NULL)
DT_ENTRY_PTR(DT_HASH, lo->lo_hash->os_addr);
/* DT_HASH, DT_STRTAB, DT_SYMTAB, DT_STRSZ and DT_SYMENT */
if (lo->lo_dynsym != NULL && lo->lo_dynstr != NULL) {
DT_ENTRY_PTR(DT_STRTAB, lo->lo_dynstr->os_addr);
DT_ENTRY_PTR(DT_SYMTAB, lo->lo_dynsym->os_addr);
DT_ENTRY_VAL(DT_STRSZ, ld_strtab_getsize(ld->ld_dynstr));
DT_ENTRY_VAL(DT_SYMENT,
lo->lo_ec == ELFCLASS32 ? sizeof(Elf32_Sym) :
sizeof(Elf64_Sym));
}
/* DT_RPATH */
if (!STAILQ_EMPTY(&ld->ld_state.ls_rplist))
DT_ENTRY_VAL(DT_RPATH, lo->lo_rpath_nameindex);
/* DT_DEBUG */
if (!ld->ld_dso)
DT_ENTRY_VAL(DT_DEBUG, 0);
/* DT_PLTGOT, DT_PLTRELSZ, DT_PLTREL and DT_JMPREL. */
if (lo->lo_gotplt != NULL)
DT_ENTRY_PTR(DT_PLTGOT, lo->lo_gotplt->os_addr);
if (lo->lo_rel_plt != NULL) {
DT_ENTRY_VAL(DT_PLTRELSZ, lo->lo_rel_plt->os_size);
DT_ENTRY_VAL(DT_PLTREL,
ld->ld_arch->reloc_is_rela ? DT_RELA : DT_REL);
DT_ENTRY_PTR(DT_JMPREL, lo->lo_rel_plt->os_addr);
}
/* DT_REL/DT_RELA, DT_RELSZ/DT_RELASZ and DT_RELENT/DT_RELAENT */
if (lo->lo_rel_dyn != NULL) {
if (!ld->ld_arch->reloc_is_rela) {
DT_ENTRY_PTR(DT_REL, lo->lo_rel_dyn->os_addr);
DT_ENTRY_VAL(DT_RELSZ, lo->lo_rel_dyn->os_size);
DT_ENTRY_VAL(DT_RELENT, ld->ld_arch->reloc_entsize);
} else {
DT_ENTRY_PTR(DT_RELA, lo->lo_rel_dyn->os_addr);
DT_ENTRY_VAL(DT_RELASZ, lo->lo_rel_dyn->os_size);
DT_ENTRY_VAL(DT_RELAENT, ld->ld_arch->reloc_entsize);
}
}
/*
* DT_VERNEED, DT_VERNEEDNUM, DT_VERDEF, DT_VERDEFNUM and
* DT_VERSYM.
*/
if (lo->lo_verdef != NULL) {
DT_ENTRY_PTR(DT_VERDEF, lo->lo_verdef->os_addr);
DT_ENTRY_VAL(DT_VERDEFNUM, lo->lo_verdef_num);
}
if (lo->lo_verneed != NULL) {
DT_ENTRY_PTR(DT_VERNEED, lo->lo_verneed->os_addr);
DT_ENTRY_VAL(DT_VERNEEDNUM, lo->lo_verneed_num);
}
if (lo->lo_versym != NULL)
DT_ENTRY_PTR(DT_VERSYM, lo->lo_versym->os_addr);
/* DT_RELCOUNT/DT_RELACOUNT. */
if (ld->ld_state.ls_relative_reloc > 0)
DT_ENTRY_VAL(ld->ld_arch->reloc_is_rela ? DT_RELACOUNT :
DT_RELCOUNT, ld->ld_state.ls_relative_reloc);
/* Fill in the space left with DT_NULL entries */
DT_ENTRY_NULL;
}
static void
_create_dynsym_and_dynstr_section(struct ld *ld, struct ld_output *lo)
{
struct ld_output_section *os;
char dynsym_name[] = ".dynsym";
char dynstr_name[] = ".dynstr";
/*
* Create .dynsym section.
*/
HASH_FIND_STR(lo->lo_ostbl, dynsym_name, os);
if (os == NULL)
os = ld_layout_insert_output_section(ld, dynsym_name,
SHF_ALLOC);
os->os_type = SHT_DYNSYM;
os->os_flags = SHF_ALLOC;
if (lo->lo_ec == ELFCLASS32) {
os->os_entsize = sizeof(Elf32_Sym);
os->os_align = 4;
} else {
os->os_entsize = sizeof(Elf64_Sym);
os->os_align = 8;
}
lo->lo_dynsym = os;
(void) ld_output_create_section_element(ld, os, OET_SYMTAB,
ld->ld_dynsym, NULL);
/*
* Create .dynstr section.
*/
HASH_FIND_STR(lo->lo_ostbl, dynstr_name, os);
if (os == NULL)
os = ld_layout_insert_output_section(ld, dynstr_name,
SHF_ALLOC);
os->os_type = SHT_STRTAB;
os->os_flags = SHF_ALLOC;
os->os_entsize = 0;
os->os_align = 1;
lo->lo_dynstr = os;
(void) ld_output_create_section_element(ld, os, OET_STRTAB,
ld->ld_dynstr, NULL);
if ((lo->lo_dynsym->os_link = strdup(".dynstr")) == NULL)
ld_fatal_std(ld, "strdup");
}
static void
_check_dso_needed(struct ld *ld, struct ld_output *lo)
{
struct ld_input *li;
char *bn;
int ndx;
lo->lo_dso_needed = 0;
STAILQ_FOREACH(li, &ld->ld_lilist, li_next) {
if (li->li_type != LIT_DSO)
continue;
if (li->li_dso_refcnt > 0 || !li->li_file->lf_as_needed) {
lo->lo_dso_needed++;
if (ld->ld_dynstr == NULL)
ld->ld_dynstr = ld_strtab_alloc(ld, 0);
/* Insert DSO name to the .dynstr string table. */
if (li->li_soname != NULL)
bn = li->li_soname;
else {
if ((bn = strrchr(li->li_name, '/')) == NULL)
bn = li->li_name;
else
bn++;
}
ndx = ld_strtab_insert_no_suffix(ld, ld->ld_dynstr,
bn);
/* Save the generated name index for later use. */
if (lo->lo_dso_nameindex == NULL)
utarray_new(lo->lo_dso_nameindex, &ut_int_icd);
utarray_push_back(lo->lo_dso_nameindex, &ndx);
}
}
}

34
ld/ld_dynamic.h Normal file
View File

@ -0,0 +1,34 @@
/*-
* Copyright (c) 2012 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: ld_dynamic.h 2784 2012-12-15 19:19:48Z kaiwang27 $
*/
void ld_dynamic_create(struct ld *);
void ld_dynamic_finalize(struct ld *);
void ld_dynamic_load_dso_dynamic(struct ld *, struct ld_input *, Elf *,
Elf_Scn *, size_t);
void ld_dynamic_create_copy_reloc(struct ld *);
void ld_dynamic_reserve_dynbss_entry(struct ld *, struct ld_symbol *);

770
ld/ld_ehframe.c Normal file
View File

@ -0,0 +1,770 @@
/*-
* Copyright (c) 2009-2013 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "ld.h"
#include "ld_arch.h"
#include "ld_ehframe.h"
#include "ld_input.h"
#include "ld_output.h"
#include "ld_reloc.h"
#include "ld_utils.h"
ELFTC_VCSID("$Id: ld_ehframe.c 2964 2013-09-10 02:46:06Z kaiwang27 $");
struct ld_ehframe_cie {
uint64_t cie_off; /* offset in section */
uint64_t cie_off_orig; /* orignial offset (before optimze) */
uint64_t cie_size; /* CIE size (include length field) */
uint8_t *cie_content; /* CIE content */
uint8_t cie_fde_encode; /* FDE PC start/range encode. */
struct ld_ehframe_cie *cie_dup; /* duplicate entry */
STAILQ_ENTRY(ld_ehframe_cie) cie_next;
};
STAILQ_HEAD(ld_ehframe_cie_head, ld_ehframe_cie);
struct ld_ehframe_fde {
struct ld_ehframe_cie *fde_cie; /* associated CIE */
uint64_t fde_off; /* offset in section */
uint64_t fde_off_pcbegin; /* section offset of "PC Begin" field */
int32_t fde_pcrel; /* relative offset to "PC Begin" */
int32_t fde_datarel; /* relative offset to FDE entry */
STAILQ_ENTRY(ld_ehframe_fde) fde_next;
};
STAILQ_HEAD(ld_ehframe_fde_head, ld_ehframe_fde);
static int64_t _decode_sleb128(uint8_t **dp);
static uint64_t _decode_uleb128(uint8_t **dp);
static void _process_ehframe_section(struct ld *ld, struct ld_output *lo,
struct ld_input_section *is);
static int _read_encoded(struct ld *ld, struct ld_output *lo, uint64_t *val,
uint8_t *data, uint8_t encode, uint64_t pc);
static int _cmp_fde(struct ld_ehframe_fde *a, struct ld_ehframe_fde *b);
void
ld_ehframe_adjust(struct ld *ld, struct ld_input_section *is)
{
struct ld_output *lo;
uint8_t *p, *d, *end, *s;
uint64_t length, length_size, remain, adjust;
uint32_t cie_id;
lo = ld->ld_output;
assert(lo != NULL);
/*
* If the .eh_frame section is unchanged, we don't need to
* do much.
*/
assert(is->is_ehframe != NULL);
if (is->is_shrink == 0) {
is->is_ehframe = NULL;
return;
}
/*
* Otherwise the section is shrinked becase some FDE's are
* discarded. We copy the section content to a buffer while
* skipping those discarded FDE's.
*/
if ((is->is_ibuf = malloc(is->is_size - is->is_shrink)) == NULL)
ld_fatal_std(ld, "malloc");
d = is->is_ibuf;
end = d + is->is_size - is->is_shrink;
p = is->is_ehframe;
adjust = 0;
remain = is->is_size;
while (remain > 0) {
s = p;
/* Read CIE/FDE length field. */
READ_32(p, length);
p += 4;
if (length == 0xffffffff) {
READ_64(p, length);
p += 8;
length_size = 8;
} else
length_size = 4;
/* Check for terminator */
if (length == 0) {
memset(d, 0, 4);
d += 4;
break;
}
/* Read CIE ID/Pointer field. */
READ_32(p, cie_id);
/* Clear adjustment if CIE is found. */
if (cie_id == 0)
adjust = 0;
/* Check for our special mark. */
if (cie_id != 0xFFFFFFFF) {
if (cie_id != 0) {
/* Adjust FDE pointer. */
assert(cie_id > adjust);
cie_id -= adjust;
WRITE_32(p, cie_id);
}
memcpy(d, s, length + length_size);
d += length + length_size;
} else {
/* Discard FDE and increate adjustment. */
adjust += length + length_size;
}
/* Next entry. */
p += length;
remain -= length + length_size;
}
is->is_size -= is->is_shrink;
is->is_shrink = 0;
assert(d == end);
free(is->is_ehframe);
is->is_ehframe = NULL;
}
void
ld_ehframe_scan(struct ld *ld)
{
struct ld_output *lo;
struct ld_output_section *os;
struct ld_output_element *oe;
struct ld_input_section *is;
struct ld_input_section_head *islist;
uint64_t ehframe_off;
char ehframe_name[] = ".eh_frame";
lo = ld->ld_output;
assert(lo != NULL);
/*
* Search for .eh_frame output section. Nothing needs to be done
* if .eh_frame section not exist or is empty.
*/
HASH_FIND_STR(lo->lo_ostbl, ehframe_name, os);
if (os == NULL || os->os_empty)
return;
if ((ld->ld_cie = malloc(sizeof(*ld->ld_cie))) == NULL)
ld_fatal_std(ld, "malloc");
STAILQ_INIT(ld->ld_cie);
/*
* Remove duplicate CIE from each input .eh_frame section.
*/
ehframe_off = 0;
STAILQ_FOREACH(oe, &os->os_e, oe_next) {
/*
* XXX We currently do not support .eh_frame section which
* contains elements other than OET_INPUT_SECTION_LIST.
*/
if (oe->oe_type != OET_INPUT_SECTION_LIST)
continue;
islist = oe->oe_islist;
STAILQ_FOREACH(is, islist, is_next) {
/*
* Process each input .eh_frame section and search
* for duplicate CIE's. The input section relative
* offset in the output section is resync'ed since
* the input section might be shrinked.
*/
is->is_reloff = ehframe_off;
_process_ehframe_section(ld, lo, is);
ehframe_off += is->is_size;
}
}
/* Calculate the size of .eh_frame_hdr section. */
if (ld->ld_ehframe_hdr) {
is = ld_input_find_internal_section(ld, ".eh_frame_hdr");
assert(is != NULL);
if (lo->lo_fde_num > 0)
is->is_size += 4 + lo->lo_fde_num * 8;
}
}
void
ld_ehframe_create_hdr(struct ld *ld)
{
struct ld_input_section *is;
is = ld_input_add_internal_section(ld, ".eh_frame_hdr");
is->is_type = SHT_PROGBITS;
is->is_size = 8; /* initial size */
is->is_align = 4;
is->is_entsize = 0;
}
void
ld_ehframe_finalize_hdr(struct ld *ld)
{
struct ld_input_section *is, *hdr_is;
struct ld_input_section_head *islist;
struct ld_output *lo;
struct ld_output_section *os, *hdr_os;
struct ld_output_element *oe;
struct ld_ehframe_fde *fde, *_fde;
char ehframe_name[] = ".eh_frame";
uint64_t pcbegin;
int32_t pcrel;
uint8_t *p, *end;
lo = ld->ld_output;
assert(lo != NULL);
hdr_is = ld_input_find_internal_section(ld, ".eh_frame_hdr");
assert(hdr_is != NULL);
hdr_os = hdr_is->is_output;
lo->lo_ehframe_hdr = hdr_os;
if (hdr_is->is_discard || hdr_os == NULL)
return;
p = hdr_is->is_ibuf;
end = p + hdr_is->is_size;
/* Find .eh_frame output section. */
HASH_FIND_STR(lo->lo_ostbl, ehframe_name, os);
assert(os != NULL);
/* .eh_frame_hdr version */
*p++ = 1;
/*
* eh_frame_ptr_enc: encoding format for eh_frame_ptr field.
* Usually a signed 4-byte PC relateive offset is used here.
*/
*p++ = DW_EH_PE_pcrel | DW_EH_PE_sdata4;
/*
* fde_count_enc: encoding format for fde_count field. Unsigned
* 4 byte encoding should be used here. Note that If the binary
* search table is not present, DW_EH_PE_omit should be used
* instead.
*/
*p++ = lo->lo_fde_num == 0 ? DW_EH_PE_omit : DW_EH_PE_udata4;
/*
* table_enc: encoding format for the binary search table entry.
* Signed 4 byte table relative offset is used here. Note that
* if the binary search table is not present, DW_EH_PE_omit should
* be used instaed.
*/
*p++ = lo->lo_fde_num == 0 ? DW_EH_PE_omit :
(DW_EH_PE_datarel | DW_EH_PE_sdata4);
/* Write 4 byte PC relative offset to the .eh_frame section. */
pcrel = os->os_addr - hdr_os->os_addr - 4;
WRITE_32(p, pcrel);
p += 4;
/* Write the total number of FDE's. */
WRITE_32(p, lo->lo_fde_num);
p += 4;
/* Allocate global FDE list. */
if (ld->ld_fde == NULL) {
if ((ld->ld_fde = calloc(1, sizeof(ld->ld_fde))) == NULL)
ld_fatal_std(ld, "calloc");
STAILQ_INIT(ld->ld_fde);
}
/* Link together the FDE's from each input object. */
STAILQ_FOREACH(oe, &os->os_e, oe_next) {
if (oe->oe_type != OET_INPUT_SECTION_LIST)
continue;
islist = oe->oe_islist;
STAILQ_FOREACH(is, islist, is_next) {
if (is->is_fde == NULL || STAILQ_EMPTY(is->is_fde))
continue;
STAILQ_FOREACH_SAFE(fde, is->is_fde, fde_next, _fde) {
(void) _read_encoded(ld, lo, &pcbegin,
(uint8_t *) is->is_ibuf +
fde->fde_off_pcbegin,
fde->fde_cie->cie_fde_encode, os->os_addr);
fde->fde_pcrel = pcbegin - hdr_os->os_addr;
fde->fde_datarel = os->os_addr +
is->is_reloff + fde->fde_off -
hdr_os->os_addr;
STAILQ_REMOVE(is->is_fde, fde, ld_ehframe_fde,
fde_next);
STAILQ_INSERT_TAIL(ld->ld_fde, fde, fde_next);
}
}
}
/* Sort the binary search table in an increasing order by pcrel. */
STAILQ_SORT(ld->ld_fde, ld_ehframe_fde, fde_next, _cmp_fde);
/* Write binary search table. */
STAILQ_FOREACH(fde, ld->ld_fde, fde_next) {
WRITE_32(p, fde->fde_pcrel);
p += 4;
WRITE_32(p, fde->fde_datarel);
p += 4;
}
assert(p == end);
}
static int
_cmp_fde(struct ld_ehframe_fde *a, struct ld_ehframe_fde *b)
{
if (a->fde_pcrel < b->fde_pcrel)
return (-1);
else if (a->fde_pcrel == b->fde_pcrel)
return (0);
else
return (1);
}
static void
_parse_cie_augment(struct ld *ld, struct ld_ehframe_cie *cie, uint8_t *aug_p,
uint8_t *augdata_p, uint64_t auglen)
{
uint64_t dummy;
uint8_t encode, *augdata_end;
int len;
assert(aug_p != NULL && *aug_p == 'z');
augdata_end = augdata_p + auglen;
/*
* Here we're only interested in the presence of augment 'R'
* and associated CIE augment data, which describes the
* encoding scheme of FDE PC begin and range.
*/
aug_p++;
while (*aug_p != '\0') {
switch (*aug_p) {
case 'L':
/* Skip one augment in augment data. */
augdata_p++;
break;
case 'P':
/* Skip two augments in augment data. */
encode = *augdata_p++;
len = _read_encoded(ld, ld->ld_output, &dummy,
augdata_p, encode, 0);
augdata_p += len;
break;
case 'R':
cie->cie_fde_encode = *augdata_p++;
break;
default:
ld_warn(ld, "unsupported eh_frame augmentation `%c'",
*aug_p);
return;
}
aug_p++;
}
if (augdata_p > augdata_end)
ld_warn(ld, "invalid eh_frame augmentation");
}
static void
_process_ehframe_section(struct ld *ld, struct ld_output *lo,
struct ld_input_section *is)
{
struct ld_input *li;
struct ld_output_section *os;
struct ld_ehframe_cie *cie, *_cie;
struct ld_ehframe_cie_head cie_h;
struct ld_ehframe_fde *fde;
struct ld_reloc_entry *lre, *_lre;
uint64_t length, es, off, off_orig, remain, shrink, auglen;
uint32_t cie_id, cie_pointer, length_size;
uint8_t *p, *et, cie_version, *augment;
li = is->is_input;
os = is->is_output;
STAILQ_INIT(&cie_h);
/*
* .eh_frame section content should already be preloaded
* in is->is_ibuf.
*/
assert(is->is_ibuf != NULL && is->is_size > 0);
shrink = 0;
p = is->is_ibuf;
off = off_orig = 0;
remain = is->is_size;
while (remain > 0) {
et = p;
off = et - (uint8_t *) is->is_ibuf;
/* Read CIE/FDE length field. */
READ_32(p, length);
p += 4;
es = length + 4;
if (length == 0xffffffff) {
READ_64(p, length);
p += 8;
es += 8;
length_size = 8;
} else
length_size = 4;
/* Check for terminator */
if (length == 0)
break;
/* Read CIE ID/Pointer field. */
READ_32(p, cie_id);
p += 4;
if (cie_id == 0) {
/* This is a Common Information Entry (CIE). */
if ((cie = calloc(1, sizeof(*cie))) == NULL)
ld_fatal_std(ld, "calloc");
cie->cie_off = off;
cie->cie_off_orig = off_orig;
cie->cie_size = es;
cie->cie_content = et;
cie->cie_dup = NULL;
STAILQ_INSERT_TAIL(&cie_h, cie, cie_next);
/*
* This is a Common Information Entry (CIE). Search
* in the CIE list see if we can found a duplicate
* entry.
*/
STAILQ_FOREACH(_cie, ld->ld_cie, cie_next) {
if (memcmp(et, _cie->cie_content, es) == 0) {
cie->cie_dup = _cie;
break;
}
}
if (_cie != NULL) {
/*
* We found a duplicate entry. It should be
* removed and the subsequent FDE's should
* point to the previously stored CIE.
*/
memmove(et, et + es, remain - es);
shrink += es;
p = et;
} else {
/*
* This is a new CIE entry which should be
* kept. Read its augmentation which is
* used to parse assoicated FDE's later.
*/
cie_version = *p++;
if (cie_version != 1) {
ld_warn(ld, "unsupported CIE version");
goto ignore_cie;
}
augment = p;
if (*p != 'z') {
ld_warn(ld, "unsupported CIE "
"augmentation");
goto ignore_cie;
}
while (*p++ != '\0')
;
/* Skip EH Data field. */
if (strstr((char *)augment, "eh") != NULL)
p += lo->lo_ec == ELFCLASS32 ? 4 : 8;
/* Skip CAF and DAF. */
(void) _decode_uleb128(&p);
(void) _decode_sleb128(&p);
/* Skip RA. */
p++;
/* Parse augmentation data. */
auglen = _decode_uleb128(&p);
_parse_cie_augment(ld, cie, augment, p,
auglen);
ignore_cie:
p = et + es;
}
} else {
/*
* This is a Frame Description Entry (FDE). First
* Search for the associated CIE.
*/
STAILQ_FOREACH(cie, &cie_h, cie_next) {
if (cie->cie_off_orig ==
off_orig + length_size - cie_id)
break;
}
/*
* If we can not found the associated CIE, this FDE
* is invalid and we ignore it.
*/
if (cie == NULL) {
ld_warn(ld, "%s(%s): malformed FDE",
li->li_name, is->is_name);
p = et + es;
goto next_entry;
}
/* Allocate new FDE entry. */
if ((fde = calloc(1, sizeof(*fde))) == NULL)
ld_fatal_std(ld, "calloc");
fde->fde_off = off;
fde->fde_off_pcbegin = off + length_size + 4;
if (is->is_fde == NULL) {
is->is_fde = calloc(1, sizeof(*is->is_fde));
if (is->is_fde == NULL)
ld_fatal_std(ld, "calloc");
STAILQ_INIT(is->is_fde);
}
STAILQ_INSERT_TAIL(is->is_fde, fde, fde_next);
lo->lo_fde_num++;
/* Calculate the new CIE pointer value. */
if (cie->cie_dup != NULL) {
cie_pointer = off + length_size +
is->is_reloff - cie->cie_dup->cie_off;
fde->fde_cie = cie->cie_dup;
} else {
cie_pointer = off + length_size - cie->cie_off;
fde->fde_cie = cie;
}
/* Rewrite CIE pointer value. */
if (cie_id != cie_pointer) {
p -= 4;
WRITE_32(p, cie_pointer);
}
p = et + es;
}
next_entry:
off_orig += es;
remain -= es;
}
/*
* Update the relocation entry offsets since we shrinked the
* section content.
*/
if (shrink > 0 && is->is_ris != NULL && is->is_ris->is_reloc != NULL) {
STAILQ_FOREACH_SAFE(lre, is->is_ris->is_reloc, lre_next,
_lre) {
STAILQ_FOREACH(cie, &cie_h, cie_next) {
if (cie->cie_off_orig > lre->lre_offset)
break;
if (cie->cie_dup == NULL)
continue;
/*
* Remove relocations for the duplicated CIE
* entries.
*/
if (lre->lre_offset <
cie->cie_off_orig + cie->cie_size) {
STAILQ_REMOVE(is->is_ris->is_reloc,
lre, ld_reloc_entry, lre_next);
is->is_ris->is_num_reloc--;
is->is_ris->is_size -=
ld->ld_arch->reloc_entsize;
if (os->os_r != NULL)
os->os_r->os_size -=
ld->ld_arch->reloc_entsize;
break;
}
/* Adjust relocation offset for FDE entries. */
lre->lre_offset -= cie->cie_size;
}
}
}
/* Insert newly found non-duplicate CIE's to the global CIE list. */
STAILQ_FOREACH_SAFE(cie, &cie_h, cie_next, _cie) {
STAILQ_REMOVE(&cie_h, cie, ld_ehframe_cie, cie_next);
if (cie->cie_dup == NULL) {
cie->cie_off += is->is_reloff;
STAILQ_INSERT_TAIL(ld->ld_cie, cie, cie_next);
}
}
/* Update the size of input .eh_frame section */
is->is_size -= shrink;
}
static int
_read_encoded(struct ld *ld, struct ld_output *lo, uint64_t *val,
uint8_t *data, uint8_t encode, uint64_t pc)
{
int16_t s16;
int32_t s32;
uint8_t application, *begin;
int len;
if (encode == DW_EH_PE_omit)
return (0);
application = encode & 0xf0;
encode &= 0x0f;
len = 0;
begin = data;
switch (encode) {
case DW_EH_PE_absptr:
if (lo->lo_ec == ELFCLASS32)
READ_32(data, *val);
else
READ_64(data, *val);
break;
case DW_EH_PE_uleb128:
*val = _decode_uleb128(&data);
len = data - begin;
break;
case DW_EH_PE_udata2:
READ_16(data, *val);
len = 2;
break;
case DW_EH_PE_udata4:
READ_32(data, *val);
len = 4;
break;
case DW_EH_PE_udata8:
READ_64(data, *val);
len = 8;
break;
case DW_EH_PE_sleb128:
*val = _decode_sleb128(&data);
len = data - begin;
break;
case DW_EH_PE_sdata2:
READ_16(data, s16);
*val = s16;
len = 2;
break;
case DW_EH_PE_sdata4:
READ_32(data, s32);
*val = s32;
len = 4;
break;
case DW_EH_PE_sdata8:
READ_64(data, *val);
len = 8;
break;
default:
ld_warn(ld, "unsupported eh_frame encoding");
break;
}
if (application == DW_EH_PE_pcrel) {
/*
* Value is relative to .eh_frame section virtual addr.
*/
switch (encode) {
case DW_EH_PE_uleb128:
case DW_EH_PE_udata2:
case DW_EH_PE_udata4:
case DW_EH_PE_udata8:
*val += pc;
break;
case DW_EH_PE_sleb128:
case DW_EH_PE_sdata2:
case DW_EH_PE_sdata4:
case DW_EH_PE_sdata8:
*val = pc + (int64_t) *val;
break;
default:
/* DW_EH_PE_absptr is absolute value. */
break;
}
}
/* XXX Applications other than DW_EH_PE_pcrel are not handled. */
return (len);
}
static int64_t
_decode_sleb128(uint8_t **dp)
{
int64_t ret = 0;
uint8_t b;
int shift = 0;
uint8_t *src = *dp;
do {
b = *src++;
ret |= ((b & 0x7f) << shift);
shift += 7;
} while ((b & 0x80) != 0);
if (shift < 32 && (b & 0x40) != 0)
ret |= (-1 << shift);
*dp = src;
return (ret);
}
static uint64_t
_decode_uleb128(uint8_t **dp)
{
uint64_t ret = 0;
uint8_t b;
int shift = 0;
uint8_t *src = *dp;
do {
b = *src++;
ret |= ((b & 0x7f) << shift);
shift += 7;
} while ((b & 0x80) != 0);
*dp = src;
return (ret);
}

32
ld/ld_ehframe.h Normal file
View File

@ -0,0 +1,32 @@
/*-
* Copyright (c) 2013 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: ld_ehframe.h 2960 2013-08-25 03:13:07Z kaiwang27 $
*/
void ld_ehframe_adjust(struct ld *, struct ld_input_section *);
void ld_ehframe_scan(struct ld *);
void ld_ehframe_create_hdr(struct ld *);
void ld_ehframe_finalize_hdr(struct ld *);

95
ld/ld_error.c Normal file
View File

@ -0,0 +1,95 @@
/*-
* Copyright (c) 2010-2013 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "ld.h"
ELFTC_VCSID("$Id: ld_error.c 2895 2013-01-15 23:05:31Z kaiwang27 $");
/*
* Support routines for error and warning message generation.
*/
void
ld_fatal(struct ld *ld, const char *fmt, ...)
{
va_list ap;
fprintf(stderr, "%s: ", ld->ld_progname);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fputc('\n', stderr);
exit(EXIT_FAILURE);
}
void
ld_fatal_std(struct ld *ld, const char *fmt, ...)
{
va_list ap;
fprintf(stderr, "%s: ", ld->ld_progname);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, ": %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
void
ld_err(struct ld *ld, const char *fmt, ...)
{
va_list ap;
fprintf(stderr, "%s: ", ld->ld_progname);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fputc('\n', stderr);
}
void
ld_warn(struct ld *ld, const char *fmt, ...)
{
va_list ap;
fprintf(stderr, "%s: warning: ", ld->ld_progname);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fputc('\n', stderr);
}
void
ld_info(struct ld *ld, const char *fmt, ...)
{
va_list ap;
fprintf(stdout, "%s: ", ld->ld_progname);
va_start(ap, fmt);
vfprintf(stdout, fmt, ap);
va_end(ap);
fputc('\n', stdout);
}

714
ld/ld_exp.c Normal file
View File

@ -0,0 +1,714 @@
/*-
* Copyright (c) 2011,2012 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "ld.h"
#include "ld_arch.h"
#include "ld_script.h"
#include "ld_exp.h"
#include "ld_layout.h"
ELFTC_VCSID("$Id: ld_exp.c 2526 2012-07-17 17:43:30Z kaiwang27 $");
/*
* Support routines for ldscript expression.
*/
static struct ld_exp *_alloc_exp(struct ld *ld);
static int64_t _assignment(struct ld *ld, struct ld_exp *le);
static int64_t _func_addr(struct ld *ld, struct ld_exp *le);
static int64_t _func_align(struct ld *ld, struct ld_exp *le);
static int64_t _func_alignof(struct ld *ld, struct ld_exp *le);
static int64_t _func_data_segment_align(struct ld *ld, struct ld_exp *le);
static int64_t _func_data_segment_end(struct ld *ld, struct ld_exp *le);
static int64_t _func_data_segment_relro_end(struct ld *ld, struct ld_exp *le);
static int64_t _func_defined(struct ld *ld, struct ld_exp *le);
static int64_t _func_length(struct ld *ld, struct ld_exp *le);
static int64_t _func_loadaddr(struct ld *ld, struct ld_exp *le);
static int64_t _func_max(struct ld *ld, struct ld_exp *le);
static int64_t _func_min(struct ld *ld, struct ld_exp *le);
static int64_t _func_next(struct ld *ld, struct ld_exp *le);
static int64_t _func_origin(struct ld *ld, struct ld_exp *le);
static int64_t _func_segment_start(struct ld *ld, struct ld_exp *le);
static int64_t _func_sizeof(struct ld *ld, struct ld_exp *le);
static int64_t _func_sizeof_headers(struct ld *ld);
static int64_t _symbol_val(struct ld *ld, char *name);
static int64_t _symbolic_constant(struct ld *ld, const char *name);
#define _EXP_EVAL(x) ld_exp_eval(ld, (x))
#define _EXP_DUMP(x) ld_exp_dump(ld, (x))
void
ld_exp_free(struct ld_exp *le)
{
if (le == NULL)
return;
ld_exp_free(le->le_e1);
ld_exp_free(le->le_e2);
ld_exp_free(le->le_e3);
if (le->le_assign != NULL)
ld_script_assign_free(le->le_assign);
if (le->le_name != NULL)
free(le->le_name);
free(le);
}
struct ld_exp *
ld_exp_unary(struct ld *ld, enum ld_exp_op op, struct ld_exp *e1)
{
struct ld_exp *le;
le = _alloc_exp(ld);
le->le_op = op;
le->le_e1 = e1;
return (le);
}
struct ld_exp *
ld_exp_binary(struct ld *ld, enum ld_exp_op op, struct ld_exp *e1,
struct ld_exp *e2)
{
struct ld_exp *le;
le = _alloc_exp(ld);
le->le_op = op;
le->le_e1 = e1;
le->le_e2 = e2;
return (le);
}
struct ld_exp *
ld_exp_trinary(struct ld *ld, struct ld_exp *e1, struct ld_exp *e2,
struct ld_exp *e3)
{
struct ld_exp *le;
le = _alloc_exp(ld);
le->le_op = LEOP_TRINARY;
le->le_e1 = e1;
le->le_e2 = e2;
le->le_e3 = e3;
return (le);
}
struct ld_exp *
ld_exp_sizeof_headers(struct ld *ld)
{
struct ld_exp *le;
le = _alloc_exp(ld);
le->le_op = LEOP_SIZEOF_HEADERS;
return (le);
}
struct ld_exp *
ld_exp_constant(struct ld *ld, int64_t val)
{
struct ld_exp *le;
le = _alloc_exp(ld);
le->le_op = LEOP_CONSTANT;
le->le_val = val;
return (le);
}
struct ld_exp *
ld_exp_symbolic_constant(struct ld *ld, const char *name)
{
struct ld_exp *le;
le = _alloc_exp(ld);
le->le_op = LEOP_SYMBOLIC_CONSTANT;
le->le_name = strdup(name);
if (le->le_name == NULL)
ld_fatal_std(ld, "calloc");
return (le);
}
struct ld_exp *
ld_exp_symbol(struct ld *ld, const char *name)
{
struct ld_exp *le;
le = _alloc_exp(ld);
le->le_op = LEOP_SYMBOL;
le->le_name = strdup(name);
if (le->le_name == NULL)
ld_fatal_std(ld, "calloc");
return (le);
}
struct ld_exp *
ld_exp_name(struct ld *ld, const char *name)
{
struct ld_exp *le;
le = _alloc_exp(ld);
le->le_op = LEOP_SECTION_NAME;
le->le_name = strdup(name);
if (le->le_name == NULL)
ld_fatal_std(ld, "calloc");
return (le);
}
struct ld_exp *
ld_exp_assign(struct ld *ld, struct ld_script_assign *assign)
{
struct ld_exp *le;
le = _alloc_exp(ld);
le->le_op = LEOP_ASSIGN;
le->le_assign = assign;
return (le);
}
int64_t
ld_exp_eval(struct ld* ld, struct ld_exp *le)
{
assert(le != NULL);
switch (le->le_op) {
case LEOP_ABS:
return (abs(_EXP_EVAL(le->le_e1)));
case LEOP_ADD:
return (_EXP_EVAL(le->le_e1) + _EXP_EVAL(le->le_e2));
case LEOP_ADDR:
return (_func_addr(ld, le));
case LEOP_ALIGN:
case LEOP_BLOCK:
return (_func_align(ld, le));
case LEOP_ALIGNOF:
return (_func_alignof(ld, le));
case LEOP_AND:
return (_EXP_EVAL(le->le_e1) & _EXP_EVAL(le->le_e2));
case LEOP_ASSIGN:
return (_assignment(ld, le));
case LEOP_CONSTANT:
return (le->le_val);
case LEOP_DIV:
return (_EXP_EVAL(le->le_e1) / _EXP_EVAL(le->le_e2));
case LEOP_DSA:
return (_func_data_segment_align(ld, le));
case LEOP_DSE:
return (_func_data_segment_end(ld, le));
case LEOP_DSRE:
return (_func_data_segment_relro_end(ld, le));
case LEOP_DEFINED:
return (_func_defined(ld, le));
case LEOP_EQUAL:
return (_EXP_EVAL(le->le_e1) == _EXP_EVAL(le->le_e2));
case LEOP_GE:
return (_EXP_EVAL(le->le_e1) >= _EXP_EVAL(le->le_e2));
case LEOP_GREATER:
return (_EXP_EVAL(le->le_e1) > _EXP_EVAL(le->le_e2));
case LEOP_LENGTH:
return (_func_length(ld, le));
case LEOP_LOADADDR:
return (_func_loadaddr(ld, le));
case LEOP_LOGICAL_AND:
return (_EXP_EVAL(le->le_e1) && _EXP_EVAL(le->le_e2));
case LEOP_LOGICAL_OR:
return (_EXP_EVAL(le->le_e1) || _EXP_EVAL(le->le_e2));
case LEOP_LSHIFT:
return (_EXP_EVAL(le->le_e1) << _EXP_EVAL(le->le_e2));
case LEOP_MAX:
return (_func_max(ld, le));
case LEOP_MIN:
return (_func_min(ld, le));
case LEOP_MINUS:
return (-(_EXP_EVAL(le->le_e1)));
case LEOP_MOD:
return (_EXP_EVAL(le->le_e1) % _EXP_EVAL(le->le_e2));
case LEOP_MUL:
return (_EXP_EVAL(le->le_e1) * _EXP_EVAL(le->le_e2));
case LEOP_NE:
return (_EXP_EVAL(le->le_e1) != _EXP_EVAL(le->le_e2));
case LEOP_NEGATION:
return (~(_EXP_EVAL(le->le_e1)));
case LEOP_NEXT:
return (_func_next(ld, le));
case LEOP_NOT:
return (!(_EXP_EVAL(le->le_e1)));
case LEOP_OR:
return (_EXP_EVAL(le->le_e1) | _EXP_EVAL(le->le_e2));
case LEOP_ORIGIN:
return (_func_origin(ld, le));
case LEOP_RSHIFT:
return (_EXP_EVAL(le->le_e1) >> _EXP_EVAL(le->le_e2));
case LEOP_SEGMENT_START:
return (_func_segment_start(ld, le));
case LEOP_SIZEOF:
return (_func_sizeof(ld, le));
case LEOP_SIZEOF_HEADERS:
return (_func_sizeof_headers(ld));
case LEOP_SUBSTRACT:
return (_EXP_EVAL(le->le_e1) - _EXP_EVAL(le->le_e2));
case LEOP_SYMBOL:
return (_symbol_val(ld, le->le_name));
case LEOP_SYMBOLIC_CONSTANT:
return (_symbolic_constant(ld, le->le_name));
case LEOP_TRINARY:
return (_EXP_EVAL(le->le_e1) ? _EXP_EVAL(le->le_e2) :
_EXP_EVAL(le->le_e3));
default:
ld_fatal(ld, "internal: unknown ldscript expression op");
}
return (0);
}
void
ld_exp_dump(struct ld *ld, struct ld_exp *le)
{
assert(le != NULL);
if (le->le_par)
printf("(");
switch (le->le_op) {
case LEOP_ABS:
printf("ABS(");
_EXP_DUMP(le->le_e1);
printf(")");
break;
case LEOP_ADD:
_EXP_DUMP(le->le_e1);
printf(" + ");
_EXP_DUMP(le->le_e2);
break;
case LEOP_ADDR:
printf("ADDR(");
_EXP_DUMP(le->le_e1);
printf(")");
break;
case LEOP_ALIGN:
case LEOP_BLOCK:
printf("ALIGN(");
_EXP_DUMP(le->le_e1);
if (le->le_e2 != NULL) {
printf(", ");
_EXP_DUMP(le->le_e2);
}
printf(")");
break;
case LEOP_ALIGNOF:
printf("ALIGNOF(");
_EXP_DUMP(le->le_e1);
printf(")");
break;
case LEOP_AND:
_EXP_DUMP(le->le_e1);
printf(" & ");
_EXP_DUMP(le->le_e2);
break;
case LEOP_ASSIGN:
printf("0x%jx", (uintmax_t) le->le_assign->lda_res);
break;
case LEOP_CONSTANT:
printf("0x%jx", (uintmax_t) le->le_val);
break;
case LEOP_DIV:
_EXP_DUMP(le->le_e1);
printf(" / ");
_EXP_DUMP(le->le_e2);
break;
case LEOP_DSA:
printf("DATA_SEGMENT_ALIGN(");
_EXP_DUMP(le->le_e1);
printf(", ");
_EXP_DUMP(le->le_e2);
printf(")");
break;
case LEOP_DSE:
printf("DATA_SEGMENT_END(");
_EXP_DUMP(le->le_e1);
printf(")");
break;
case LEOP_DSRE:
printf("DATA_SEGMENT_RELRO_END(");
_EXP_DUMP(le->le_e1);
printf(", ");
_EXP_DUMP(le->le_e2);
printf(")");
break;
case LEOP_DEFINED:
printf("DEFINED(");
_EXP_DUMP(le->le_e1);
printf(")");
break;
case LEOP_EQUAL:
_EXP_DUMP(le->le_e1);
printf(" == ");
_EXP_DUMP(le->le_e2);
break;
case LEOP_GE:
_EXP_DUMP(le->le_e1);
printf(" >= ");
_EXP_DUMP(le->le_e2);
break;
case LEOP_GREATER:
_EXP_DUMP(le->le_e1);
printf(" > ");
_EXP_DUMP(le->le_e2);
break;
case LEOP_LENGTH:
printf("LENGTH(");
_EXP_DUMP(le->le_e1);
printf(")");
break;
case LEOP_LOADADDR:
printf("LOADADDR(");
_EXP_DUMP(le->le_e1);
printf(")");
break;
case LEOP_LOGICAL_AND:
_EXP_DUMP(le->le_e1);
printf(" && ");
_EXP_DUMP(le->le_e2);
break;
case LEOP_LOGICAL_OR:
_EXP_DUMP(le->le_e1);
printf(" || ");
_EXP_DUMP(le->le_e2);
break;
case LEOP_LSHIFT:
_EXP_DUMP(le->le_e1);
printf(" << ");
_EXP_DUMP(le->le_e2);
break;
case LEOP_MAX:
printf("MAX(");
_EXP_DUMP(le->le_e1);
printf(", ");
_EXP_DUMP(le->le_e2);
printf(")");
break;
case LEOP_MIN:
printf("MIN(");
_EXP_DUMP(le->le_e1);
printf(", ");
_EXP_DUMP(le->le_e2);
printf(")");
break;
case LEOP_MINUS:
printf("-");
_EXP_DUMP(le->le_e1);
break;
case LEOP_MOD:
_EXP_DUMP(le->le_e1);
printf(" %% ");
_EXP_DUMP(le->le_e2);
break;
case LEOP_MUL:
_EXP_DUMP(le->le_e1);
printf(" * ");
_EXP_DUMP(le->le_e2);
break;
case LEOP_NE:
_EXP_DUMP(le->le_e1);
printf(" != ");
_EXP_DUMP(le->le_e2);
break;
case LEOP_NEGATION:
printf("~");
_EXP_DUMP(le->le_e1);
break;
case LEOP_NEXT:
printf("NEXT(");
_EXP_DUMP(le->le_e1);
printf(")");
break;
case LEOP_NOT:
printf("!");
_EXP_DUMP(le->le_e1);
break;
case LEOP_OR:
_EXP_DUMP(le->le_e1);
printf(" | ");
_EXP_DUMP(le->le_e2);
break;
case LEOP_ORIGIN:
printf("ORIGIN(");
_EXP_DUMP(le->le_e1);
printf(")");
break;
case LEOP_RSHIFT:
_EXP_DUMP(le->le_e1);
printf(" >> ");
_EXP_DUMP(le->le_e2);
break;
case LEOP_SEGMENT_START:
printf("SEGMENT_START(");
_EXP_DUMP(le->le_e1);
printf(", ");
_EXP_DUMP(le->le_e2);
printf(")");
break;
case LEOP_SIZEOF:
printf("SIZEOF(");
_EXP_DUMP(le->le_e1);
printf(")");
break;
case LEOP_SIZEOF_HEADERS:
printf("SIZEOF_HEADERS");
break;
case LEOP_SUBSTRACT:
_EXP_DUMP(le->le_e1);
printf(" - ");
_EXP_DUMP(le->le_e2);
break;
case LEOP_SYMBOL:
printf("%s", le->le_name);
break;
case LEOP_SYMBOLIC_CONSTANT:
printf("0x%jx",
(uintmax_t) _symbolic_constant(ld, le->le_name));
break;
case LEOP_TRINARY:
_EXP_DUMP(le->le_e1);
printf(" ? ");
_EXP_DUMP(le->le_e2);
printf(" : ");
_EXP_DUMP(le->le_e3);
break;
default:
ld_fatal(ld, "internal: unknown ldscript expression op");
}
if (le->le_par)
printf(")");
}
static struct ld_exp *
_alloc_exp(struct ld *ld)
{
struct ld_exp *le;
if ((le = calloc(1, sizeof(*le))) == NULL)
ld_fatal_std(ld, "calloc");
return (le);
}
static int64_t
_assignment(struct ld *ld, struct ld_exp *le)
{
struct ld_exp *var;
assert(le->le_assign != NULL);
ld_script_process_assign(ld, le->le_assign);
var = le->le_assign->lda_var;
return (ld_script_variable_value(ld, var->le_name));
}
static int64_t
_func_addr(struct ld *ld, struct ld_exp *le)
{
/* TODO */
(void) ld; (void) le;
return (0);
}
static int64_t
_func_align(struct ld *ld, struct ld_exp *le)
{
struct ld_state *ls;
ls = &ld->ld_state;
if (le->le_e2 != NULL)
return (roundup(_EXP_EVAL(le->le_e1), _EXP_EVAL(le->le_e2)));
else
return (roundup(ls->ls_loc_counter, _EXP_EVAL(le->le_e1)));
}
static int64_t
_func_alignof(struct ld *ld, struct ld_exp *le)
{
/* TODO */
(void) ld; (void) le;
return (0);
}
static int64_t
_func_data_segment_align(struct ld *ld, struct ld_exp *le)
{
struct ld_state *ls;
uint64_t maxpagesize, commonpagesize;
/*
* TODO: test if align to common page size use less number
* of pages.
*/
ls = &ld->ld_state;
maxpagesize = _EXP_EVAL(le->le_e1);
commonpagesize = _EXP_EVAL(le->le_e2);
return (roundup(ls->ls_loc_counter, maxpagesize) +
(ls->ls_loc_counter & (maxpagesize - 1)));
}
static int64_t
_func_data_segment_end(struct ld *ld, struct ld_exp *le)
{
return (_EXP_EVAL(le->le_e1));
}
static int64_t
_func_data_segment_relro_end(struct ld *ld, struct ld_exp *le)
{
/* TODO */
(void) ld; (void) le;
return (0);
}
static int64_t
_func_defined(struct ld *ld, struct ld_exp *le)
{
/* TODO */
(void) ld; (void) le;
return (0);
}
static int64_t
_func_length(struct ld *ld, struct ld_exp *le)
{
/* TODO */
(void) ld; (void) le;
return (0);
}
static int64_t
_func_loadaddr(struct ld *ld, struct ld_exp *le)
{
/* TODO */
(void) ld; (void) le;
return (0);
}
static int64_t
_func_max(struct ld *ld, struct ld_exp *le)
{
uint64_t val1, val2;
val1 = _EXP_EVAL(le->le_e1);
val2 = _EXP_EVAL(le->le_e2);
return (val1 > val2 ? val1 : val2);
}
static int64_t
_func_min(struct ld *ld, struct ld_exp *le)
{
uint64_t val1, val2;
val1 = _EXP_EVAL(le->le_e1);
val2 = _EXP_EVAL(le->le_e2);
return (val1 > val2 ? val2 : val1);
}
static int64_t
_func_next(struct ld *ld, struct ld_exp *le)
{
/* TODO */
(void) ld; (void) le;
return (0);
}
static int64_t
_func_origin(struct ld *ld, struct ld_exp *le)
{
/* TODO */
(void) ld; (void) le;
return (0);
}
static int64_t
_func_segment_start(struct ld *ld, struct ld_exp *le)
{
/* TODO */
(void) ld; (void) le;
return (0);
}
static int64_t
_func_sizeof(struct ld *ld, struct ld_exp *le)
{
/* TODO */
(void) ld; (void) le;
return (0);
}
static int64_t
_func_sizeof_headers(struct ld *ld)
{
return (ld_layout_calc_header_size(ld));
}
static int64_t
_symbol_val(struct ld *ld, char *name)
{
return (ld_script_variable_value(ld, name));
}
static int64_t
_symbolic_constant(struct ld *ld, const char *name)
{
if (ld->ld_arch == NULL)
return (0);
if (strcmp(name, "COMMONPAGESIZE") == 0)
return (ld->ld_arch->get_common_page_size(ld));
else if (strcmp(name, "MAXPAGESIZE") == 0)
return (ld->ld_arch->get_max_page_size(ld));
return (0);
}

100
ld/ld_exp.h Normal file
View File

@ -0,0 +1,100 @@
/*-
* Copyright (c) 2011,2012 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: ld_exp.h 2525 2012-07-17 17:36:19Z kaiwang27 $
*/
enum ld_exp_op {
LEOP_ABS,
LEOP_ADD,
LEOP_ADDR,
LEOP_ALIGN,
LEOP_ALIGNOF,
LEOP_AND,
LEOP_ASSIGN,
LEOP_BLOCK,
LEOP_CONSTANT,
LEOP_DIV,
LEOP_DSA,
LEOP_DSE,
LEOP_DSRE,
LEOP_DEFINED,
LEOP_EQUAL,
LEOP_GE,
LEOP_GREATER,
LEOP_LENGTH,
LEOP_LE,
LEOP_LESSER,
LEOP_LOADADDR,
LEOP_LOGICAL_AND,
LEOP_LOGICAL_OR,
LEOP_LSHIFT,
LEOP_MAX,
LEOP_MIN,
LEOP_MINUS,
LEOP_MOD,
LEOP_MUL,
LEOP_NE,
LEOP_NEGATION,
LEOP_NEXT,
LEOP_NOT,
LEOP_OR,
LEOP_ORIGIN,
LEOP_RSHIFT,
LEOP_SEGMENT_START,
LEOP_SIZEOF,
LEOP_SIZEOF_HEADERS,
LEOP_SECTION_NAME,
LEOP_SUBSTRACT,
LEOP_SYMBOL,
LEOP_SYMBOLIC_CONSTANT,
LEOP_TRINARY,
};
struct ld_exp {
enum ld_exp_op le_op; /* expression operator */
struct ld_exp *le_e1; /* fisrt operand */
struct ld_exp *le_e2; /* second operand */
struct ld_exp *le_e3; /* third operand */
struct ld_script_assign *le_assign; /* assignment */
char *le_name; /* symbol/section name */
unsigned le_par; /* parenthesis */
int64_t le_val; /* constant value */
};
struct ld_exp *ld_exp_assign(struct ld *, struct ld_script_assign *);
struct ld_exp *ld_exp_binary(struct ld *, enum ld_exp_op, struct ld_exp *,
struct ld_exp *);
struct ld_exp *ld_exp_constant(struct ld *, int64_t);
int64_t ld_exp_eval(struct ld *, struct ld_exp *);
void ld_exp_dump(struct ld *, struct ld_exp *);
struct ld_exp *ld_exp_name(struct ld *, const char *);
struct ld_exp *ld_exp_sizeof_headers(struct ld *);
struct ld_exp *ld_exp_symbol(struct ld *, const char *);
struct ld_exp *ld_exp_symbolic_constant(struct ld *, const char *);
struct ld_exp *ld_exp_trinary(struct ld *, struct ld_exp *, struct ld_exp *,
struct ld_exp *);
struct ld_exp *ld_exp_unary(struct ld *, enum ld_exp_op, struct ld_exp *);
void ld_exp_free(struct ld_exp *);

233
ld/ld_file.c Normal file
View File

@ -0,0 +1,233 @@
/*-
* Copyright (c) 2010-2013 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "ld.h"
#include "ld_arch.h"
#include "ld_file.h"
#include "ld_path.h"
ELFTC_VCSID("$Id: ld_file.c 2930 2013-03-17 22:54:26Z kaiwang27 $");
/*
* Support routines for input file handling.
*/
static void _add_file(struct ld *ld, const char *name, enum ld_file_type type,
int first, struct ld_file *after);
void
ld_file_cleanup(struct ld *ld)
{
struct ld_file *lf, *_lf;
struct ld_archive_member *lam, *_lam;
TAILQ_FOREACH_SAFE(lf, &ld->ld_lflist, lf_next, _lf) {
TAILQ_REMOVE(&ld->ld_lflist, lf, lf_next);
free(lf->lf_name);
if (lf->lf_ar != NULL) {
HASH_ITER(hh, lf->lf_ar->la_m, lam, _lam) {
HASH_DEL(lf->lf_ar->la_m, lam);
free(lam->lam_name);
free(lam);
}
free(lf->lf_ar);
}
free(lf);
}
}
void
ld_file_add(struct ld *ld, const char *name, enum ld_file_type type)
{
_add_file(ld, name, type, 0, NULL);
}
void
ld_file_add_first(struct ld *ld, const char *name, enum ld_file_type type)
{
_add_file(ld, name, type, 1, NULL);
}
void
ld_file_add_after(struct ld *ld, const char *name, enum ld_file_type type,
struct ld_file *after)
{
_add_file(ld, name, type, 0, after);
}
void
ld_file_load(struct ld *ld, struct ld_file *lf)
{
struct ld_archive *la;
struct ld_state *ls;
struct stat sb;
Elf_Kind k;
GElf_Ehdr ehdr;
int fd;
assert(lf != NULL && lf->lf_name != NULL);
ls = &ld->ld_state;
if (ls->ls_file == lf)
return;
if ((fd = open(lf->lf_name, O_RDONLY)) < 0)
ld_fatal_std(ld, "%s: open", lf->lf_name);
if (fstat(fd, &sb) < 0)
ld_fatal_std(ld, "%s: stat", lf->lf_name);
if (sb.st_size == 0)
ld_fatal(ld, "%s: File truncated", lf->lf_name);
lf->lf_size = sb.st_size;
if ((lf->lf_mmap = mmap(NULL, lf->lf_size, PROT_READ, MAP_PRIVATE, fd,
(off_t) 0)) == MAP_FAILED)
ld_fatal_std(ld, "%s: mmap", lf->lf_name);
close(fd);
if (lf->lf_type == LFT_BINARY)
return;
if ((lf->lf_elf = elf_memory(lf->lf_mmap, lf->lf_size)) == NULL)
ld_fatal(ld, "%s: elf_memory failed: %s", lf->lf_name,
elf_errmsg(-1));
k = elf_kind(lf->lf_elf);
if (k == ELF_K_AR) {
lf->lf_type = LFT_ARCHIVE;
if (lf->lf_ar == NULL) {
if ((la = calloc(1, sizeof(*la))) == NULL)
ld_fatal_std(ld, "calloc");
lf->lf_ar = la;
}
return;
}
assert(k != ELF_K_AR);
if (k == ELF_K_NONE)
ld_fatal(ld, "%s: File format not recognized", lf->lf_name);
if (gelf_getehdr(lf->lf_elf, &ehdr) == NULL)
ld_fatal(ld, "%s: gelf_getehdr failed: %s", lf->lf_name,
elf_errmsg(-1));
switch (ehdr.e_type) {
case ET_NONE:
ld_fatal(ld, "%s: ELF type ET_NONE not supported", lf->lf_name);
case ET_REL:
lf->lf_type = LFT_RELOCATABLE;
break;
case ET_EXEC:
ld_fatal(ld, "%s: ELF type ET_EXEC not supported yet",
lf->lf_name);
case ET_DYN:
lf->lf_type = LFT_DSO;
break;
case ET_CORE:
ld_fatal(ld, "%s: ELF type ET_NONE not supported", lf->lf_name);
default:
ld_fatal(ld, "%s: unknown ELF type %u", ehdr.e_type);
}
ld_arch_verify(ld, lf->lf_name, ehdr.e_machine);
}
void
ld_file_unload(struct ld *ld, struct ld_file *lf)
{
struct ld_state *ls;
ls = &ld->ld_state;
if (lf->lf_type != LFT_BINARY)
elf_end(lf->lf_elf);
if (lf->lf_mmap != NULL) {
if (munmap(lf->lf_mmap, lf->lf_size) < 0)
ld_fatal_std(ld, "%s: munmap", lf->lf_name);
}
if (ls->ls_file == lf)
ls->ls_file = NULL;
}
static void
_add_file(struct ld *ld, const char *name, enum ld_file_type type,
int first, struct ld_file *after)
{
struct ld_state *ls;
struct ld_file *lf;
int fd;
assert(ld != NULL && name != NULL);
if (!strncmp(name, "-l", 2)) {
ld_path_search_library(ld, &name[2]);
return;
}
ls = &ld->ld_state;
if ((lf = calloc(1, sizeof(*lf))) == NULL)
ld_fatal_std(ld, "calloc");
if ((lf->lf_name = strdup(name)) == NULL)
ld_fatal_std(ld, "strdup");
lf->lf_type = type;
lf->lf_whole_archive = ls->ls_whole_archive;
lf->lf_as_needed = ls->ls_as_needed;
lf->lf_group_level = ls->ls_group_level;
lf->lf_search_dir = ls->ls_search_dir;
if ((fd = open(lf->lf_name, O_RDONLY)) < 0) {
if (!lf->lf_search_dir)
ld_fatal_std(ld, "%s: open", lf->lf_name);
/* Search library path for this file. */
ld_path_search_file(ld, lf);
} else
(void) close(fd);
if (lf->lf_type == LFT_UNKNOWN && ls->ls_itgt != NULL) {
if (elftc_bfd_target_flavor(ls->ls_itgt) == ETF_BINARY)
lf->lf_type = LFT_BINARY;
}
if (lf->lf_type == LFT_DSO)
ld->ld_dynamic_link = 1;
if (after != NULL)
TAILQ_INSERT_AFTER(&ld->ld_lflist, after, lf, lf_next);
else if (first)
TAILQ_INSERT_HEAD(&ld->ld_lflist, lf, lf_next);
else
TAILQ_INSERT_TAIL(&ld->ld_lflist, lf, lf_next);
}

70
ld/ld_file.h Normal file
View File

@ -0,0 +1,70 @@
/*-
* Copyright (c) 2010-2013 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: ld_file.h 2930 2013-03-17 22:54:26Z kaiwang27 $
*/
enum ld_file_type {
LFT_UNKNOWN,
LFT_RELOCATABLE,
LFT_DSO,
LFT_ARCHIVE,
LFT_BINARY
};
struct ld_archive_member {
char *lam_ar_name; /* archive name */
char *lam_name; /* archive member name */
off_t lam_off; /* archive member offset */
struct ld_input *lam_input; /* input object */
UT_hash_handle hh; /* hash handle */
};
struct ld_archive {
struct ld_archive_member *la_m; /* extracted member list. */
};
struct ld_file {
char *lf_name; /* input file name */
enum ld_file_type lf_type; /* input file type */
void *lf_mmap; /* input file image */
size_t lf_size; /* input file size */
Elf *lf_elf; /* input file ELF descriptor */
struct ld_archive *lf_ar; /* input archive */
struct ld_input *lf_input; /* input object */
unsigned lf_whole_archive; /* include whole archive */
unsigned lf_as_needed; /* DT_NEEDED */
unsigned lf_group_level; /* archive group level */
unsigned lf_search_dir; /* search library directories */
TAILQ_ENTRY(ld_file) lf_next; /* next input file */
};
void ld_file_add(struct ld *, const char *, enum ld_file_type);
void ld_file_add_first(struct ld *, const char *, enum ld_file_type);
void ld_file_add_after(struct ld *, const char *, enum ld_file_type,
struct ld_file *);
void ld_file_cleanup(struct ld *);
void ld_file_load(struct ld *, struct ld_file *);
void ld_file_unload(struct ld *, struct ld_file *);

126
ld/ld_hash.c Normal file
View File

@ -0,0 +1,126 @@
/*-
* Copyright (c) 2012 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "ld.h"
#include "ld_hash.h"
#include "ld_layout.h"
#include "ld_output.h"
#include "ld_symbols.h"
ELFTC_VCSID("$Id: ld_hash.c 2917 2013-02-16 07:16:02Z kaiwang27 $");
/*
* The number of buckets to use for a certain number of symbols.
* If there are less than 3 symbols, 1 bucket will be used. If
* there are less than 17 symbols, 3 buckets will be used, and so
* forth. The bucket numbers are defined by GNU ld. We use the
* same rules here so we generate hash sections with the same
* size as those generated by GNU ld.
*/
static unsigned hash_buckets[] = {
1, 3, 17, 37, 67, 97, 131, 197, 263, 521, 1031, 2053, 4099, 8209,
16411, 32771, 65537, 131101, 262147
};
void
ld_hash_create_svr4_hash_section(struct ld *ld)
{
struct ld_output *lo;
struct ld_output_section *os;
struct ld_output_data_buffer *odb;
struct ld_symbol *lsb;
char hash_name[] = ".hash";
uint32_t *buf, *buckets, *chains, nbuckets, nchains;
int i, j;
lo = ld->ld_output;
assert(lo != NULL);
HASH_FIND_STR(lo->lo_ostbl, hash_name, os);
if (os == NULL)
os = ld_layout_insert_output_section(ld, hash_name, SHF_ALLOC);
os->os_type = SHT_HASH;
os->os_flags = SHF_ALLOC;
os->os_entsize = 4;
if (lo->lo_ec == ELFCLASS32)
os->os_align = 4;
else
os->os_align = 8;
if ((os->os_link = strdup(".dynsym")) == NULL)
ld_fatal_std(ld, "strdup");
lo->lo_hash = os;
assert(ld->ld_dynsym != NULL && ld->ld_dynsym->sy_size > 0);
nchains = ld->ld_dynsym->sy_size;
nbuckets = 0;
for (i = 1;
(size_t) i < sizeof(hash_buckets) / sizeof(hash_buckets[0]);
i++) {
if (nchains < hash_buckets[i]) {
nbuckets = hash_buckets[i - 1];
break;
}
}
if (nbuckets == 0)
nbuckets = hash_buckets[i - 1];
if ((buf = calloc(nbuckets + nchains + 2, sizeof(uint32_t))) == NULL)
ld_fatal_std(ld, "calloc");
buf[0] = nbuckets;
buf[1] = nchains;
buckets = &buf[2];
chains = &buf[2 + nbuckets];
assert(ld->ld_dyn_symbols != NULL);
i = 1;
STAILQ_FOREACH(lsb, ld->ld_dyn_symbols, lsb_dyn) {
if (lsb->lsb_name == NULL) {
i++;
continue;
}
j = elf_hash(lsb->lsb_name) % nbuckets;
chains[i] = buckets[j];
buckets[j] = i;
i++;
}
if ((odb = calloc(1, sizeof(*odb))) == NULL)
ld_fatal_std(ld, "calloc");
odb->odb_buf = (void *) buf;
odb->odb_size = (nbuckets + nchains + 2) * sizeof(uint32_t);
odb->odb_align = os->os_align;
odb->odb_type = ELF_T_WORD; /* enable libelf translation */
(void) ld_output_create_section_element(ld, os, OET_DATA_BUFFER,
odb, NULL);
}

29
ld/ld_hash.h Normal file
View File

@ -0,0 +1,29 @@
/*-
* Copyright (c) 2012 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: ld_hash.h 2669 2012-11-11 13:20:43Z kaiwang27 $
*/
void ld_hash_create_svr4_hash_section(struct ld *);

653
ld/ld_input.c Normal file
View File

@ -0,0 +1,653 @@
/*-
* Copyright (c) 2011-2013 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "ld.h"
#include "ld_file.h"
#include "ld_input.h"
#include "ld_symbols.h"
ELFTC_VCSID("$Id: ld_input.c 2960 2013-08-25 03:13:07Z kaiwang27 $");
/*
* Support routines for input section handling.
*/
static void _discard_section_group(struct ld *ld, struct ld_input *li,
Elf_Scn *scn);
static off_t _offset_sort(struct ld_archive_member *a,
struct ld_archive_member *b);
#define _MAX_INTERNAL_SECTIONS 16
void
ld_input_init(struct ld *ld)
{
struct ld_input *li;
struct ld_input_section *is;
assert(STAILQ_EMPTY(&ld->ld_lilist));
/*
* Create an internal pseudo input object to hold internal
* input sections.
*/
li = ld_input_alloc(ld, NULL, NULL);
li->li_is = calloc(_MAX_INTERNAL_SECTIONS,
sizeof(struct ld_input_section));
if (li->li_is == NULL)
ld_fatal_std(ld, "calloc");
STAILQ_INSERT_TAIL(&ld->ld_lilist, li, li_next);
/*
* Create an initial SHT_NULL section for the pseudo input object,
* so all the internal sections will have valid section index.
* (other than SHN_UNDEF)
*/
is = &li->li_is[li->li_shnum];
if ((is->is_name = strdup("")) == NULL)
ld_fatal_std(ld, "strdup");
is->is_input = li;
is->is_type = SHT_NULL;
is->is_index = li->li_shnum;
li->li_shnum++;
}
struct ld_input_section *
ld_input_add_internal_section(struct ld *ld, const char *name)
{
struct ld_input *li;
struct ld_input_section *is;
li = STAILQ_FIRST(&ld->ld_lilist);
assert(li != NULL);
if (li->li_shnum >= _MAX_INTERNAL_SECTIONS)
ld_fatal(ld, "Internal: not enough buffer for internal "
"sections");
is = &li->li_is[li->li_shnum];
if ((is->is_name = strdup(name)) == NULL)
ld_fatal_std(ld, "strdup");
is->is_input = li;
is->is_index = li->li_shnum;
/* Use a hash table to accelerate lookup for internal sections. */
HASH_ADD_KEYPTR(hh, li->li_istbl, is->is_name, strlen(is->is_name),
is);
li->li_shnum++;
return (is);
}
struct ld_input_section *
ld_input_find_internal_section(struct ld *ld, const char *name)
{
struct ld_input *li;
struct ld_input_section *is;
char _name[32];
li = STAILQ_FIRST(&ld->ld_lilist);
assert(li != NULL);
snprintf(_name, sizeof(_name), "%s", name);
HASH_FIND_STR(li->li_istbl, _name, is);
return (is);
}
uint64_t
ld_input_reserve_ibuf(struct ld_input_section *is, uint64_t n)
{
uint64_t off;
assert(is->is_entsize != 0);
off = is->is_size;
is->is_size += n * is->is_entsize;
return (off);
}
void
ld_input_alloc_internal_section_buffers(struct ld *ld)
{
struct ld_input *li;
struct ld_input_section *is;
int i;
li = STAILQ_FIRST(&ld->ld_lilist);
assert(li != NULL);
for (i = 0; (uint64_t) i < li->li_shnum; i++) {
is = &li->li_is[i];
if (is->is_type == SHT_NOBITS || is->is_size == 0 ||
is->is_dynrel)
continue;
if ((is->is_ibuf = malloc(is->is_size)) == NULL)
ld_fatal_std(ld, "malloc");
}
}
void
ld_input_cleanup(struct ld *ld)
{
struct ld_input *li, *_li;
int i;
STAILQ_FOREACH_SAFE(li, &ld->ld_lilist, li_next, _li) {
STAILQ_REMOVE(&ld->ld_lilist, li, ld_input, li_next);
if (li->li_symindex)
free(li->li_symindex);
if (li->li_local)
free(li->li_local);
if (li->li_versym)
free(li->li_versym);
if (li->li_vername) {
for (i = 0; (size_t) i < li->li_vername_sz; i++)
if (li->li_vername[i])
free(li->li_vername[i]);
free(li->li_vername);
}
if (li->li_is)
free(li->li_is);
if (li->li_fullname)
free(li->li_fullname);
if (li->li_name)
free(li->li_name);
if (li->li_soname)
free(li->li_soname);
free(li);
}
}
void
ld_input_add_symbol(struct ld *ld, struct ld_input *li, struct ld_symbol *lsb)
{
if (li->li_symindex == NULL) {
assert(li->li_symnum != 0);
li->li_symindex = calloc(li->li_symnum,
sizeof(*li->li_symindex));
if (li->li_symindex == NULL)
ld_fatal_std(ld, "calloc");
}
li->li_symindex[lsb->lsb_index] = lsb;
if (lsb->lsb_bind == STB_LOCAL) {
if (li->li_local == NULL) {
li->li_local = calloc(1, sizeof(*li->li_local));
if (li->li_local == NULL)
ld_fatal_std(ld, "calloc");
STAILQ_INIT(li->li_local);
}
STAILQ_INSERT_TAIL(li->li_local, lsb, lsb_next);
}
}
struct ld_input *
ld_input_alloc(struct ld *ld, struct ld_file *lf, const char *name)
{
struct ld_input *li;
if ((li = calloc(1, sizeof(*li))) == NULL)
ld_fatal_std(ld, "calloc");
if (name != NULL && (li->li_name = strdup(name)) == NULL)
ld_fatal_std(ld, "strdup");
li->li_file = lf;
if (lf != NULL) {
switch (lf->lf_type) {
case LFT_ARCHIVE:
case LFT_RELOCATABLE:
li->li_type = LIT_RELOCATABLE;
break;
case LFT_DSO:
li->li_type = LIT_DSO;
break;
case LFT_BINARY:
case LFT_UNKNOWN:
default:
li->li_type = LIT_UNKNOWN;
break;
}
} else
li->li_type = LIT_RELOCATABLE;
return (li);
}
char *
ld_input_get_fullname(struct ld *ld, struct ld_input *li)
{
struct ld_archive_member *lam;
size_t len;
if (li->li_fullname != NULL)
return (li->li_fullname);
if (li->li_lam == NULL)
return (li->li_name);
lam = li->li_lam;
len = strlen(lam->lam_ar_name) + strlen(lam->lam_name) + 3;
if ((li->li_fullname = malloc(len)) == NULL)
ld_fatal_std(ld, "malloc");
snprintf(li->li_fullname, len, "%s(%s)", lam->lam_ar_name,
lam->lam_name);
return (li->li_fullname);
}
void
ld_input_link_objects(struct ld *ld)
{
struct ld_file *lf;
struct ld_archive_member *lam, *tmp;
struct ld_input *li;
TAILQ_FOREACH(lf, &ld->ld_lflist, lf_next) {
if (lf->lf_ar != NULL) {
HASH_SORT(lf->lf_ar->la_m, _offset_sort);
HASH_ITER(hh, lf->lf_ar->la_m, lam, tmp) {
li = lam->lam_input;
if (li != NULL)
STAILQ_INSERT_TAIL(&ld->ld_lilist, li,
li_next);
}
} else {
li = lf->lf_input;
if (li != NULL)
STAILQ_INSERT_TAIL(&ld->ld_lilist, li, li_next);
}
}
}
void *
ld_input_get_section_rawdata(struct ld *ld, struct ld_input_section *is)
{
Elf *e;
Elf_Scn *scn;
Elf_Data *d;
struct ld_input *li;
char *buf;
int elferr;
li = is->is_input;
e = li->li_elf;
assert(e != NULL);
if ((scn = elf_getscn(e, is->is_index)) == NULL)
ld_fatal(ld, "%s(%s): elf_getscn failed: %s", li->li_name,
is->is_name, elf_errmsg(-1));
(void) elf_errno();
if ((d = elf_rawdata(scn, NULL)) == NULL) {
elferr = elf_errno();
if (elferr != 0)
ld_warn(ld, "%s(%s): elf_rawdata failed: %s",
li->li_name, is->is_name, elf_errmsg(elferr));
return (NULL);
}
if (d->d_buf == NULL || d->d_size == 0)
return (NULL);
if ((buf = malloc(d->d_size)) == NULL)
ld_fatal_std(ld, "malloc");
memcpy(buf, d->d_buf, d->d_size);
return (buf);
}
void
ld_input_load(struct ld *ld, struct ld_input *li)
{
struct ld_state *ls;
struct ld_file *lf;
struct ld_archive_member *lam;
if (li->li_file == NULL)
return;
assert(li->li_elf == NULL);
ls = &ld->ld_state;
if (li->li_file != ls->ls_file) {
if (ls->ls_file != NULL)
ld_file_unload(ld, ls->ls_file);
ld_file_load(ld, li->li_file);
}
lf = li->li_file;
if (lf->lf_ar != NULL) {
assert(li->li_lam != NULL);
lam = li->li_lam;
if (elf_rand(lf->lf_elf, lam->lam_off) != lam->lam_off)
ld_fatal(ld, "%s: elf_rand: %s", lf->lf_name,
elf_errmsg(-1));
if ((li->li_elf = elf_begin(-1, ELF_C_READ, lf->lf_elf)) ==
NULL)
ld_fatal(ld, "%s: elf_begin: %s", lf->lf_name,
elf_errmsg(-1));
} else
li->li_elf = lf->lf_elf;
}
void
ld_input_unload(struct ld *ld, struct ld_input *li)
{
struct ld_file *lf;
(void) ld;
if (li->li_file == NULL)
return;
assert(li->li_elf != NULL);
lf = li->li_file;
if (lf->lf_ar != NULL)
(void) elf_end(li->li_elf);
li->li_elf = NULL;
}
void
ld_input_init_sections(struct ld *ld, struct ld_input *li, Elf *e)
{
struct ld_input_section *is;
struct ld_section_group *sg;
Elf_Scn *scn, *_scn;
Elf_Data *d, *_d;
char *name;
GElf_Shdr sh;
GElf_Sym sym;
size_t shstrndx, strndx, ndx;
int elferr;
_d = NULL;
strndx = 0;
if (elf_getshdrnum(e, &li->li_shnum) < 0)
ld_fatal(ld, "%s: elf_getshdrnum: %s", li->li_name,
elf_errmsg(-1));
/* Allocate one more pseudo section to hold common symbols */
li->li_shnum++;
assert(li->li_is == NULL);
if ((li->li_is = calloc(li->li_shnum, sizeof(*is))) == NULL)
ld_fatal_std(ld, "%s: calloc: %s", li->li_name);
if (elf_getshdrstrndx(e, &shstrndx) < 0)
ld_fatal(ld, "%s: elf_getshdrstrndx: %s", li->li_name,
elf_errmsg(-1));
(void) elf_errno();
scn = NULL;
while ((scn = elf_nextscn(e, scn)) != NULL) {
if (gelf_getshdr(scn, &sh) != &sh)
ld_fatal(ld, "%s: gelf_getshdr: %s", li->li_name,
elf_errmsg(-1));
if ((name = elf_strptr(e, shstrndx, sh.sh_name)) == NULL)
ld_fatal(ld, "%s: elf_strptr: %s", li->li_name,
elf_errmsg(-1));
if ((ndx = elf_ndxscn(scn)) == SHN_UNDEF)
ld_fatal(ld, "%s: elf_ndxscn: %s", li->li_name,
elf_errmsg(-1));
if (ndx >= li->li_shnum - 1)
ld_fatal(ld, "%s: section index of '%s' section is"
" invalid", li->li_name, name);
is = &li->li_is[ndx];
if ((is->is_name = strdup(name)) == NULL)
ld_fatal_std(ld, "%s: calloc", li->li_name);
is->is_off = sh.sh_offset;
is->is_size = sh.sh_size;
is->is_entsize = sh.sh_entsize;
is->is_addr = sh.sh_addr;
is->is_align = sh.sh_addralign;
is->is_type = sh.sh_type;
is->is_flags = sh.sh_flags;
is->is_link = sh.sh_link;
is->is_info = sh.sh_info;
is->is_index = elf_ndxscn(scn);
is->is_shrink = 0;
is->is_input = li;
/*
* Section groups are identified by their signatures.
* A section group's signature is used to compare with the
* the section groups that are already added. If a match
* is found, the sections included in this section group
* should be discarded.
*
* Note that since signatures are stored in the symbol
* table, in order to handle that here we have to load
* the symbol table earlier.
*/
if (is->is_type == SHT_GROUP) {
is->is_discard = 1;
if (_d == NULL) {
_scn = elf_getscn(e, is->is_link);
if (_scn == NULL) {
ld_warn(ld, "%s: elf_getscn failed"
" with the `sh_link' of group"
" section %ju as index: %s",
li->li_name, ndx, elf_errmsg(-1));
continue;
}
if (gelf_getshdr(_scn, &sh) != &sh) {
ld_warn(ld, "%s: gelf_getshdr: %s",
li->li_name, elf_errmsg(-1));
continue;
}
strndx = sh.sh_link;
(void) elf_errno();
_d = elf_getdata(_scn, NULL);
if (_d == NULL) {
elferr = elf_errno();
if (elferr != 0)
ld_warn(ld, "%s: elf_getdata"
" failed: %s", li->li_name,
elf_errmsg(elferr));
continue;
}
}
if (gelf_getsym(_d, is->is_info, &sym) != &sym) {
ld_warn(ld, "%s: gelf_getsym failed (section"
" group signature): %s", li->li_name,
elf_errmsg(-1));
continue;
}
if ((name = elf_strptr(e, strndx, sym.st_name)) ==
NULL) {
ld_warn(ld, "%s: elf_strptr failed (section"
" group signature): %s", li->li_name,
elf_errmsg(-1));
continue;
}
/*
* Search the currently added section groups for the
* signature. If found, this section group should not
* be added and the sections it contains should be
* discarded. If not found, we add this section group
* to the set.
*/
HASH_FIND_STR(ld->ld_sg, name, sg);
if (sg != NULL)
_discard_section_group(ld, li, scn);
else {
if ((sg = calloc(1, sizeof(*sg))) == NULL)
ld_fatal_std(ld, "%s: calloc",
li->li_name);
if ((sg->sg_name = strdup(name)) == NULL)
ld_fatal_std(ld, "%s: strdup",
li->li_name);
HASH_ADD_KEYPTR(hh, ld->ld_sg, sg->sg_name,
strlen(sg->sg_name), sg);
}
}
/*
* Check for informational sections which should not
* be included in the output object, process them
* and mark them as discarded if need.
*/
if (strcmp(is->is_name, ".note.GNU-stack") == 0) {
ld->ld_gen_gnustack = 1;
if (is->is_flags & SHF_EXECINSTR)
ld->ld_stack_exec = 1;
is->is_discard = 1;
continue;
}
/*
* The content of input .eh_frame section is preloaded for
* output .eh_frame optimization.
*/
if (strcmp(is->is_name, ".eh_frame") == 0) {
if ((d = elf_rawdata(scn, NULL)) == NULL) {
elferr = elf_errno();
if (elferr != 0)
ld_warn(ld, "%s(%s): elf_rawdata "
"failed: %s", li->li_name,
is->is_name, elf_errmsg(elferr));
continue;
}
if (d->d_buf == NULL || d->d_size == 0)
continue;
if ((is->is_ehframe = malloc(d->d_size)) == NULL)
ld_fatal_std(ld, "malloc");
memcpy(is->is_ehframe, d->d_buf, d->d_size);
is->is_ibuf = is->is_ehframe;
}
}
elferr = elf_errno();
if (elferr != 0)
ld_fatal(ld, "%s: elf_nextscn failed: %s", li->li_name,
elf_errmsg(elferr));
}
void
ld_input_alloc_common_symbol(struct ld *ld, struct ld_symbol *lsb)
{
struct ld_input *li;
struct ld_input_section *is;
li = lsb->lsb_input;
if (li == NULL)
return; /* unlikely */
/*
* Do not allocate memory for common symbols when the linker
* creates a relocatable output object, unless option -d is
* specified.
*/
if (ld->ld_reloc && !ld->ld_common_alloc)
return;
is = &li->li_is[li->li_shnum - 1];
if (is->is_name == NULL) {
/*
* Create a pseudo section named COMMON to keep track of
* common symbols.
*/
if ((is->is_name = strdup("COMMON")) == NULL)
ld_fatal_std(ld, "%s: calloc", li->li_name);
is->is_off = 0;
is->is_size = 0;
is->is_entsize = 0;
is->is_align = 1;
is->is_type = SHT_NOBITS;
is->is_flags = SHF_ALLOC | SHF_WRITE;
is->is_link = 0;
is->is_info = 0;
is->is_index = SHN_COMMON;
is->is_input = li;
}
/*
* Allocate space for this symbol in the pseudo COMMON section.
* Properly handle the alignment. (For common symbols, symbol
* value stores the required alignment)
*/
if (lsb->lsb_value > is->is_align)
is->is_align = lsb->lsb_value;
is->is_size = roundup(is->is_size, is->is_align);
lsb->lsb_value = is->is_size;
is->is_size += lsb->lsb_size;
}
static void
_discard_section_group(struct ld *ld, struct ld_input *li, Elf_Scn *scn)
{
Elf_Data *d;
uint32_t *w;
int elferr, i;
(void) elf_errno();
if ((d = elf_getdata(scn, NULL)) == NULL) {
elferr = elf_errno();
if (elferr != 0)
ld_warn(ld, "%s: elf_getdata failed (section group):"
" %s", li->li_name, elf_errmsg(elferr));
return;
}
if (d->d_buf == NULL || d->d_size == 0)
return;
w = d->d_buf;
if ((*w & GRP_COMDAT) == 0)
return;
for (i = 1; (size_t) i < d->d_size / 4; i++) {
if (w[i] < li->li_shnum - 1)
li->li_is[w[i]].is_discard = 1;
}
}
static off_t
_offset_sort(struct ld_archive_member *a, struct ld_archive_member *b)
{
return (a->lam_off - b->lam_off);
}

121
ld/ld_input.h Normal file
View File

@ -0,0 +1,121 @@
/*-
* Copyright (c) 2011-2013 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: ld_input.h 2960 2013-08-25 03:13:07Z kaiwang27 $
*/
struct ld_reloc_entry_head;
struct ld_ehframe_fde_head;
struct ld_section_group {
char *sg_name;
UT_hash_handle hh;
};
struct ld_input_section {
char *is_name; /* section name */
struct ld_input *is_input; /* containing input object */
struct ld_output_section *is_output; /* containing output section */
uint64_t is_off; /* section file offset */
uint64_t is_reloff; /* relative offset in output section */
uint64_t is_addr; /* section vma */
uint64_t is_size; /* section file size */
uint64_t is_shrink; /* section shrinked bytes */
uint64_t is_entsize; /* seciton entry size */
uint64_t is_align; /* section align */
uint64_t is_type; /* section type */
uint64_t is_flags; /* section flags */
uint64_t is_link; /* section link */
uint64_t is_info; /* section info */
uint64_t is_index; /* section index */
unsigned char is_discard; /* dicard section */
unsigned char is_dynrel; /* section holds dynamic relocations */
unsigned char is_pltrel; /* section holds PLT relocations */
unsigned char is_refed; /* should not be gc'ed */
unsigned char is_need_reloc; /* need apply relocation */
void *is_data; /* output section data descriptor */
void *is_ibuf; /* buffer for internal sections */
void *is_ehframe; /* temp buffer for ehframe section. */
struct ld_reloc_entry_head *is_reloc; /* list of relocation entries */
uint64_t is_num_reloc; /* number of reloc entries */
struct ld_input_section *is_tis; /* relocation target */
struct ld_input_section *is_ris; /* relocation section */
struct ld_ehframe_fde_head *is_fde; /* list of FDE */
STAILQ_ENTRY(ld_input_section) is_next; /* next section */
STAILQ_ENTRY(ld_input_section) is_gc_next; /* next gc search */
UT_hash_handle hh; /* hash handle (internal section) */
};
STAILQ_HEAD(ld_input_section_head, ld_input_section);
enum ld_input_type {
LIT_UNKNOWN,
LIT_RELOCATABLE,
LIT_DSO,
};
struct ld_symver_verdef_head;
struct ld_input {
char *li_name; /* input object name */
char *li_fullname; /* input object and archive name */
char *li_soname; /* input object DT_SONAME. */
Elf *li_elf; /* input object ELF descriptor */
enum ld_input_type li_type; /* input object kind */
struct ld_file *li_file; /* containing file */
size_t li_shnum; /* num of sections in ELF object */
struct ld_input_section *li_is; /* input section list */
struct ld_input_section *li_istbl; /* internal section hash table */
struct ld_archive_member *li_lam; /* archive member */
struct ld_symbol_head *li_local; /* local symbol list */
struct ld_symbol **li_symindex; /* symbol index table */
size_t li_symnum; /* number of symbols */
char **li_vername; /* version name array */
size_t li_vername_sz; /* version name array size */
uint16_t *li_versym; /* symbol version array */
size_t li_versym_sz; /* symbol version array size */
int li_dso_refcnt; /* symbol reference count (DSO) */
struct ld_symver_verdef_head *li_verdef; /* version definition */
STAILQ_ENTRY(ld_input) li_next; /* next input object */
};
void ld_input_init(struct ld *);
void ld_input_add_symbol(struct ld *, struct ld_input *,
struct ld_symbol *);
struct ld_input_section *ld_input_add_internal_section(struct ld *,
const char *);
struct ld_input_section *ld_input_find_internal_section(struct ld *,
const char *);
void ld_input_alloc_internal_section_buffers(struct ld *);
struct ld_input *ld_input_alloc(struct ld *, struct ld_file *, const char *);
void ld_input_alloc_common_symbol(struct ld *, struct ld_symbol *);
void *ld_input_get_section_rawdata(struct ld *, struct ld_input_section *);
void ld_input_cleanup(struct ld *);
char *ld_input_get_fullname(struct ld *, struct ld_input *);
void ld_input_init_sections(struct ld *, struct ld_input *, Elf *);
void ld_input_link_objects(struct ld *);
void ld_input_load(struct ld *, struct ld_input *);
void ld_input_unload(struct ld *, struct ld_input *);
uint64_t ld_input_reserve_ibuf(struct ld_input_section *, uint64_t);

1254
ld/ld_layout.c Normal file

File diff suppressed because it is too large Load Diff

33
ld/ld_layout.h Normal file
View File

@ -0,0 +1,33 @@
/*-
* Copyright (c) 2011,2012 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: ld_layout.h 2696 2012-11-24 17:12:24Z kaiwang27 $
*/
void ld_layout_sections(struct ld *);
off_t ld_layout_calc_header_size(struct ld *);
struct ld_output_section *ld_layout_insert_output_section(struct ld *,
const char *, uint64_t);
void ld_layout_print_linkmap(struct ld *);

145
ld/ld_main.c Normal file
View File

@ -0,0 +1,145 @@
/*-
* Copyright (c) 2010-2013 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "ld.h"
#include "ld_arch.h"
#include "ld_ehframe.h"
#include "ld_options.h"
#include "ld_reloc.h"
#include "ld_script.h"
#include "ld_file.h"
#include "ld_input.h"
#include "ld_layout.h"
#include "ld_output.h"
#include "ld_path.h"
#include "ld_symbols.h"
ELFTC_VCSID("$Id: ld_main.c 2959 2013-08-25 03:12:47Z kaiwang27 $");
static struct ld _ld;
struct ld* ld = &_ld;
static void
_init(void)
{
if ((ld->ld_progname = ELFTC_GETPROGNAME()) == NULL)
ld->ld_progname = "ld";
/* Initialise libelf. */
if (elf_version(EV_CURRENT) == EV_NONE)
ld_fatal(ld, "ELF library initialization failed: %s",
elf_errmsg(-1));
/* Initialise internal data structure. */
TAILQ_INIT(&ld->ld_lflist);
STAILQ_INIT(&ld->ld_lilist);
STAILQ_INIT(&ld->ld_state.ls_lplist);
STAILQ_INIT(&ld->ld_state.ls_rplist);
STAILQ_INIT(&ld->ld_state.ls_rllist);
}
static void
_cleanup(void)
{
ld_script_cleanup(ld);
ld_symbols_cleanup(ld);
ld_path_cleanup(ld);
ld_input_cleanup(ld);
ld_file_cleanup(ld);
}
int
main(int argc, char **argv)
{
struct ld_state *ls;
_init();
ls = &ld->ld_state;
ld->ld_progname = basename(argv[0]);
ld_arch_init(ld);
restart:
/* The linker generate an executable by default */
ld->ld_exec = 1;
ld_script_init(ld);
ld_options_parse(ld, argc, argv);
ld_output_early_init(ld);
ls->ls_arch_conflict = 0;
ls->ls_first_elf_object = 1;
ld_input_init(ld);
ld_symbols_resolve(ld);
if (ls->ls_arch_conflict) {
_cleanup();
ls->ls_rerun = 1;
goto restart;
}
ld_reloc_load(ld);
/*
* Perform section garbage collection if command line option
* -gc-sections is specified. Perform deferred relocation scan
* after garbage sections are found.
*/
if (ld->ld_gc) {
ld_reloc_gc_sections(ld);
ld_reloc_deferred_scan(ld);
}
/*
* Search for undefined symbols and allocate space for common
* symbols. Copy relevant symbols to the dynamic symbol table
* if the linker is performing a dyanmic linking.
*/
ld_symbols_scan(ld);
/* Create .eh_frame_hdr section. */
if (ld->ld_ehframe_hdr)
ld_ehframe_create_hdr(ld);
ld_output_init(ld);
ld_layout_sections(ld);
ld_output_create(ld);
_cleanup();
exit(EXIT_SUCCESS);
}

507
ld/ld_options.c Normal file
View File

@ -0,0 +1,507 @@
/*-
* Copyright (c) 2010-2013 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "ld.h"
#include "ld_file.h"
#include "ld_path.h"
#include "ld_script.h"
#include "ld_symbols.h"
#include "ld_options.h"
#include "ld_output.h"
ELFTC_VCSID("$Id: ld_options.c 2926 2013-03-17 22:53:54Z kaiwang27 $");
/*
* Support routines for parsing command line options.
*/
static const char *ld_short_opts =
"b:c:de:Ef:Fgh:iI:l:L:m:MnNo:O::qrR:sStT:xXyY:u:vV()";
static struct ld_option ld_opts[] = {
{"aarchive", KEY_STATIC, ONE_DASH, NO_ARG},
{"adefault", KEY_DYNAMIC, ONE_DASH, NO_ARG},
{"ashared", KEY_DYNAMIC, ONE_DASH, NO_ARG},
{"accept-unknown-input-arch", KEY_ACCEPT_UNKNOWN, ANY_DASH, NO_ARG},
{"allow-multiple-definition", KEY_Z_MULDEFS, ANY_DASH, NO_ARG},
{"allow-shlib-undefined", KEY_ALLOW_SHLIB_UNDEF, ANY_DASH, NO_ARG},
{"assert", KEY_ASSERT, ANY_DASH, NO_ARG},
{"as-needed", KEY_AS_NEEDED, ANY_DASH, NO_ARG},
{"auxiliary", 'f', ANY_DASH, REQ_ARG},
{"build-id", KEY_BUILD_ID, ANY_DASH, OPT_ARG},
{"call_shared", KEY_DYNAMIC, ONE_DASH, NO_ARG},
{"check-sections", KEY_CHECK_SECTIONS, ANY_DASH, NO_ARG},
{"cref", KEY_CREF, ANY_DASH, NO_ARG},
{"defsym", KEY_DEFSYM, ANY_DASH, REQ_ARG},
{"demangle", KEY_DEMANGLE, ANY_DASH, OPT_ARG},
{"dc", 'd', ONE_DASH, NO_ARG},
{"dp", 'd', ONE_DASH, NO_ARG},
{"disable-new-dtags", KEY_DISABLE_NEW_DTAGS, ANY_DASH, NO_ARG},
{"discard-all", 'x', ANY_DASH, NO_ARG},
{"discard-locals", 'X', ANY_DASH, NO_ARG},
{"dn", KEY_STATIC, ONE_DASH, NO_ARG},
{"dy", KEY_DYNAMIC, ONE_DASH, NO_ARG},
{"dynamic-linker", 'I', ANY_DASH, REQ_ARG},
{"end-group", ')', ANY_DASH, NO_ARG},
{"entry", 'e', ANY_DASH, REQ_ARG},
{"error-unresolved-symbols", KEY_ERR_UNRESOLVE_SYM, ANY_DASH, NO_ARG},
{"export-dynamic", 'E', ANY_DASH, NO_ARG},
{"eh-frame-hdr", KEY_EH_FRAME_HDR, ANY_DASH, NO_ARG},
{"emit-relocs", 'q', ANY_DASH, NO_ARG},
{"emulation", 'm', ANY_DASH, REQ_ARG},
{"enable-new-dtags", KEY_ENABLE_NEW_DTAGS, ANY_DASH, NO_ARG},
{"fatal-warnings", KEY_FATAL_WARNINGS, ANY_DASH, NO_ARG},
{"filter", 'F', ANY_DASH, NO_ARG},
{"fini", KEY_FINI, ANY_DASH, NO_ARG},
{"format", 'b', ANY_DASH, REQ_ARG},
{"gc-sections", KEY_GC_SECTIONS, ANY_DASH, NO_ARG},
{"hash-style", KEY_HASH_STYLE, ANY_DASH, REQ_ARG},
{"help", KEY_HELP, ANY_DASH, NO_ARG},
{"init", KEY_INIT, ANY_DASH, REQ_ARG},
{"just-symbols", 'R', ANY_DASH, REQ_ARG},
{"library", 'l', ANY_DASH, REQ_ARG},
{"library-path", 'L', ANY_DASH, REQ_ARG},
{"mri-script", 'c', ANY_DASH, REQ_ARG},
{"nmagic", 'n', ANY_DASH, NO_ARG},
{"nostdlib", KEY_NO_STDLIB, ONE_DASH, NO_ARG},
{"no-accept-unknown-input-arch", KEY_NO_UNKNOWN, ANY_DASH, NO_ARG},
{"no-allow-shlib-undefined", KEY_NO_SHLIB_UNDEF, ANY_DASH, NO_ARG},
{"no-as-needed", KEY_NO_AS_NEEDED, ANY_DASH, NO_ARG},
{"no-check-sections", KEY_NO_CHECK_SECTIONS, ANY_DASH, NO_ARG},
{"no-define-common", KEY_NO_DEFINE_COMMON, ANY_DASH, NO_ARG},
{"no-demangle", KEY_NO_DEMANGLE, ANY_DASH, OPT_ARG},
{"no-gc-sections", KEY_NO_GC_SECTIONS, ANY_DASH, NO_ARG},
{"no-keep-memorg", KEY_NO_KEEP_MEMORY, ANY_DASH, NO_ARG},
{"no-omagic", KEY_NO_OMAGIC, ANY_DASH, NO_ARG},
{"no-print-gc-sections", KEY_NO_PRINT_GC_SECTIONS, ANY_DASH, NO_ARG},
{"no-undefined", KEY_Z_DEFS, ANY_DASH, NO_ARG},
{"no-undefined-version", KEY_NO_UNDEF_VERSION, ANY_DASH, NO_ARG},
{"no-whole-archive", KEY_NO_WHOLE_ARCHIVE, ANY_DASH, NO_ARG},
{"no-warn-mismatch", KEY_NO_WARN_MISMATCH, ANY_DASH, NO_ARG},
{"non_shared", KEY_STATIC, ONE_DASH, NO_ARG},
{"oformat", KEY_OFORMAT, TWO_DASH, REQ_ARG},
{"omagic", 'N', TWO_DASH, NO_ARG},
{"output", 'o', TWO_DASH, REQ_ARG},
{"pic-executable", KEY_PIE, ANY_DASH, NO_ARG},
{"pie", KEY_PIE, ONE_DASH, NO_ARG},
{"print-gc-sections", KEY_PRINT_GC_SECTIONS, ANY_DASH, NO_ARG},
{"print-map", 'M', ANY_DASH, NO_ARG},
{"qmagic", KEY_QMAGIC, ANY_DASH, NO_ARG},
{"relax", KEY_RELAX, ANY_DASH, NO_ARG},
{"relocatable", 'r', ANY_DASH, NO_ARG},
{"retain-symbols-file", KEY_RETAIN_SYM_FILE, ANY_DASH, REQ_ARG},
{"rpath", KEY_RPATH, ANY_DASH, REQ_ARG},
{"rpath-link", KEY_RPATH_LINK, ANY_DASH, REQ_ARG},
{"runpath", KEY_RUNPATH, ANY_DASH, REQ_ARG},
{"script", 'T', ANY_DASH, REQ_ARG},
{"section-start", KEY_SECTION_START, ANY_DASH, REQ_ARG},
{"shared", KEY_SHARED, ANY_DASH, NO_ARG},
{"soname", 'h', ONE_DASH, REQ_ARG},
{"sort-common", KEY_SORT_COMMON, ANY_DASH, NO_ARG},
{"split-by-file", KEY_SPLIT_BY_FILE, ANY_DASH, REQ_ARG},
{"split-by-reloc", KEY_SPLIT_BY_RELOC, ANY_DASH, REQ_ARG},
{"start-group", '(', ANY_DASH, NO_ARG},
{"stats", KEY_STATS, ANY_DASH, NO_ARG},
{"static", KEY_STATIC, ONE_DASH, NO_ARG},
{"strip-all", 's', ANY_DASH, NO_ARG},
{"strip-debug", 'S', ANY_DASH, NO_ARG},
{"trace", 't', ANY_DASH, NO_ARG},
{"trace_symbol", 'y', ANY_DASH, NO_ARG},
{"traditional-format", KEY_TRADITIONAL_FORMAT, ANY_DASH, NO_ARG},
{"undefined", 'u', ANY_DASH, REQ_ARG},
{"unique", KEY_UNIQUE, ANY_DASH, OPT_ARG},
{"unresolved-symbols", KEY_UNRESOLVED_SYMBOLS, ANY_DASH, REQ_ARG},
{"verbose" , 'v', ANY_DASH, NO_ARG},
{"version", KEY_VERSION, ANY_DASH, NO_ARG},
{"version-script", KEY_VERSION_SCRIPT, ANY_DASH, REQ_ARG},
{"warn-common", KEY_WARN_COMMON, ANY_DASH, NO_ARG},
{"warn-constructors", KEY_WARN_CONSTRUCTORS, ANY_DASH, NO_ARG},
{"warn-multiple-gp", KEY_WARN_MULTIPLE_GP, ANY_DASH, NO_ARG},
{"warn-once", KEY_WARN_ONCE, ANY_DASH, NO_ARG},
{"warn-section-align", KEY_WARN_SECTION_ALIGN, ANY_DASH, NO_ARG},
{"warn-shared-textrel", KEY_WARN_SHARED_TEXTREL, ANY_DASH, NO_ARG},
{"warn-unresolved-symbols", KEY_WARN_UNRESOLVE_SYM, ANY_DASH, NO_ARG},
{"whole_archive", KEY_WHOLE_ARCHIVE, ANY_DASH, NO_ARG},
{"wrap", KEY_WRAP, ANY_DASH, REQ_ARG},
{"EB", KEY_EB, ONE_DASH, NO_ARG},
{"EL", KEY_EL, ONE_DASH, NO_ARG},
{"Map", KEY_MAP, ONE_DASH, REQ_ARG},
{"Qy", KEY_QY, ONE_DASH, NO_ARG},
{"Tbss", KEY_TBSS, ONE_DASH, REQ_ARG},
{"Tdata", KEY_TDATA, ONE_DASH, REQ_ARG},
{"Ttext", KEY_TTEXT, ONE_DASH, REQ_ARG},
{"Ur", KEY_UR, ONE_DASH, NO_ARG},
{NULL, 0, 0, 0},
};
static struct ld_option ld_opts_B[] = {
{"shareable", KEY_SHARED, ONE_DASH, NO_ARG},
{"static", KEY_STATIC, ONE_DASH, NO_ARG},
{"dynamic", KEY_DYNAMIC, ONE_DASH, NO_ARG},
{"group", KEY_GROUP, ONE_DASH, NO_ARG},
{"symbolic", KEY_SYMBOLIC, ONE_DASH, NO_ARG},
{"symbolic_functions", KEY_SYMBOLIC_FUNC, ONE_DASH, NO_ARG},
};
static struct ld_option ld_opts_z[] = {
{"nodefaultlib", KEY_Z_NO_DEFAULT_LIB, ONE_DASH, NO_ARG},
{"allextract", KEY_WHOLE_ARCHIVE, ONE_DASH, NO_ARG},
{"defaultextract", KEY_Z_DEFAULT_EXTRACT, ONE_DASH, NO_ARG},
{"weakextract", KEY_Z_WEAK_EXTRACT, ONE_DASH, NO_ARG},
{"muldefs", KEY_Z_MULDEFS, ONE_DASH, NO_ARG},
{"defs", KEY_Z_DEFS, ONE_DASH, NO_ARG},
{"execstack", KEY_Z_EXEC_STACK, ONE_DASH, NO_ARG},
{"nodefs", KEY_Z_NO_DEFS, ONE_DASH, NO_ARG},
{"origin", KEY_Z_ORIGIN, ONE_DASH, NO_ARG},
{"now", KEY_Z_NOW, ONE_DASH, NO_ARG},
{"nodelete", KEY_Z_NO_DELETE, ONE_DASH, NO_ARG},
{"initfirst", KEY_Z_INIT_FIRST, ONE_DASH, NO_ARG},
{"lazyload", KEY_Z_LAZYLOAD, ONE_DASH, NO_ARG},
{"noexecstack", KEY_Z_NO_EXEC_STACK, ONE_DASH, NO_ARG},
{"nodlopen", KEY_Z_NO_DLOPEN, ONE_DASH, NO_ARG},
{"nolazyload", KEY_Z_NO_LAZYLOAD, ONE_DASH, NO_ARG},
{"ignore", KEY_Z_IGNORE, ONE_DASH, NO_ARG},
{"record", KEY_Z_RECORD, ONE_DASH, NO_ARG},
{"systemlibrary", KEY_Z_SYSTEM_LIBRARY, ONE_DASH, NO_ARG},
};
static void _copy_optarg(struct ld *ld, char **dst, char *src);
static void _process_options(struct ld *ld, int key, char *arg);
static int _parse_long_options(struct ld *, struct ld_option *, int,
int, char **, char *, enum ld_dash);
static void _print_version(struct ld *ld);
void
ld_options_parse(struct ld* ld, int argc, char **argv)
{
enum ld_dash d;
char *p, *p0, *oli;
int ac, ac0;
ac = 1;
while (ac < argc) {
p = argv[ac];
if (*p != '-' || p[1] == '\0') {
_process_options(ld, KEY_FILE, p);
ac++;
continue;
}
if (*++p == '-') {
if (p[1] == '\0') {
/* Option --. Ignore the rest of options. */
return;
}
p++;
d = TWO_DASH;
} else {
d = ONE_DASH;
if (*p == 'B' || *p == 'z') {
ac0 = ac;
if (*(p0 = p + 1) == '\0')
p0 = argv[++ac0];
ac = _parse_long_options(ld,
*p == 'B' ? ld_opts_B : ld_opts_z,
ac0, argc, argv, p0, d);
if (ac > 0)
continue;
ld_fatal(ld, "unrecognized options -%c: %s",
*p, p0);
}
}
ac0 = _parse_long_options(ld, ld_opts, ac, argc, argv, p, d);
if (ac0 > 0) {
ac = ac0;
continue;
}
if (d == TWO_DASH)
ld_fatal(ld, "unrecognized option %s", p);
/*
* Search short options.
*/
while (*p != '\0') {
if ((oli = strchr(ld_short_opts, *p)) == NULL)
ld_fatal(ld, "unrecognized option -%c", *p);
if (*++oli != ':') {
_process_options(ld, *p++, NULL);
continue;
}
if (p[1] != '\0')
_process_options(ld, *p, &p[1]);
else if (oli[1] != ':') {
if (++ac >= argc)
ld_fatal(ld, "require arg for"
" option -%c", *p);
_process_options(ld, *p, argv[ac]);
}
break;
}
ac++;
}
}
static int
_parse_long_options(struct ld *ld, struct ld_option *opts, int ac,
int argc, char **argv, char *opt, enum ld_dash dash)
{
char *equal;
size_t av_len;
int i, match;
if ((equal = strchr(opt, '=')) != NULL) {
av_len = equal - opt;
equal++;
if (*equal == '\0')
ld_fatal(ld, "no argument after =");
} else
av_len = strlen(opt);
match = 0;
for (i = 0; opts[i].lo_long != NULL; i++) {
if (opts[i].lo_dash != ANY_DASH && opts[i].lo_dash != dash)
continue;
if (strlen(opts[i].lo_long) == av_len &&
!strncmp(opt, opts[i].lo_long, av_len)) {
match = 1;
break;
}
}
if (!match)
return (-1);
switch (opts[i].lo_arg) {
case NO_ARG:
if (equal != NULL) {
ld_fatal(ld, "option %s does not accept argument",
opts[i].lo_long);
}
_process_options(ld, opts[i].lo_key, NULL);
break;
case REQ_ARG:
if (equal != NULL)
_process_options(ld, opts[i].lo_key, equal);
else {
if (++ac >= argc)
ld_fatal(ld, "require arg for option %s",
opts[i].lo_long);
_process_options(ld, opts[i].lo_key, argv[ac]);
}
break;
case OPT_ARG:
_process_options(ld, opts[i].lo_key, equal);
break;
default:
assert(0);
break;
}
return (++ac);
}
static void
_process_options(struct ld *ld, int key, char *arg)
{
struct ld_state *ls;
assert(ld != NULL);
ls = &ld->ld_state;
switch (key) {
case 'b':
ls->ls_itgt = elftc_bfd_find_target(arg);
if (ls->ls_itgt == NULL)
ld_fatal(ld, "invalid BFD target `%s'", arg);
break;
case 'd':
ld->ld_common_alloc = 1;
break;
case 'e':
_copy_optarg(ld, &ld->ld_entry, arg);
break;
case 'h':
_copy_optarg(ld, &ld->ld_soname, arg);
break;
case 'I':
_copy_optarg(ld, &ld->ld_interp, arg);
break;
case 'l':
ld_path_search_library(ld, arg);
break;
case 'L':
ld_path_add(ld, arg, LPT_L);
break;
case 'M':
ld->ld_print_linkmap = 1;
break;
case 'o':
_copy_optarg(ld, &ld->ld_output_file, arg);
break;
case 'q':
ld->ld_emit_reloc = 1;
break;
case 'r':
ld->ld_reloc = 1;
break;
case 'T':
ld_script_parse(arg);
break;
case 'u':
ld_symbols_add_extern(ld, arg);
break;
case 'v':
case 'V':
_print_version(ld);
break;
case '(':
ls->ls_group_level++;
if (ls->ls_group_level > LD_MAX_NESTED_GROUP)
ld_fatal(ld, "too many nested archive groups");
break;
case ')':
ls->ls_group_level--;
break;
case KEY_AS_NEEDED:
ls->ls_as_needed = 1;
break;
case KEY_DYNAMIC:
ls->ls_static = 0;
break;
case KEY_EH_FRAME_HDR:
ld->ld_ehframe_hdr = 1;
break;
case KEY_GC_SECTIONS:
ld->ld_gc = 1;
break;
case KEY_NO_AS_NEEDED:
ls->ls_as_needed = 0;
break;
case KEY_NO_DEFINE_COMMON:
ld->ld_common_no_alloc = 1;
break;
case KEY_NO_GC_SECTIONS:
ld->ld_gc = 0;
break;
case KEY_NO_PRINT_GC_SECTIONS:
ld->ld_gc_print = 0;
break;
case KEY_NO_WHOLE_ARCHIVE:
ls->ls_whole_archive = 0;
break;
case KEY_OFORMAT:
ld_output_format(ld, arg, arg, arg);
break;
case KEY_PIE:
ld->ld_exec = 0;
ld->ld_pie = 1;
ld->ld_dynamic_link = 1;
break;
case KEY_PRINT_GC_SECTIONS:
ld->ld_gc_print = 1;
break;
case KEY_RPATH:
ld_path_add_multiple(ld, arg, LPT_RPATH);
break;
case KEY_RPATH_LINK:
ld_path_add_multiple(ld, arg, LPT_RPATH_LINK);
break;
case KEY_SHARED:
ld->ld_exec = 0;
ld->ld_dso = 1;
ld->ld_dynamic_link = 1;
break;
case KEY_STATIC:
ls->ls_static = 1;
break;
case KEY_WHOLE_ARCHIVE:
ls->ls_whole_archive = 1;
break;
case KEY_FILE:
ld_file_add(ld, arg, LFT_UNKNOWN);
break;
case KEY_VERSION_SCRIPT:
ld_script_parse(arg);
break;
case KEY_Z_EXEC_STACK:
ld->ld_gen_gnustack = 1;
ld->ld_stack_exec_set = 1;
ld->ld_stack_exec = 1;
break;
case KEY_Z_NO_EXEC_STACK:
ld->ld_gen_gnustack = 1;
ld->ld_stack_exec_set = 1;
ld->ld_stack_exec = 0;
break;
default:
break;
}
}
static void
_print_version(struct ld *ld)
{
(void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version());
ld->ld_print_version = 1;
}
static void
_copy_optarg(struct ld *ld, char **dst, char *src)
{
if (*dst != NULL)
free(*dst);
if ((*dst = strdup(src)) == NULL)
ld_fatal_std(ld, "strdup");
}
struct ld_wildcard *
ld_wildcard_alloc(struct ld *ld)
{
struct ld_wildcard *lw;
if ((lw = calloc(1, sizeof(*lw))) == NULL)
ld_fatal_std(ld, "calloc");
return (lw);
}
void
ld_wildcard_free(void *ptr)
{
struct ld_wildcard *lw;
lw = ptr;
if (lw == NULL)
return;
free(lw->lw_name);
free(lw);
}

161
ld/ld_options.h Normal file
View File

@ -0,0 +1,161 @@
/*-
* Copyright (c) 2010-2013 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: ld_options.h 2894 2013-01-15 23:05:24Z kaiwang27 $
*/
enum ld_dash {
ONE_DASH,
TWO_DASH,
ANY_DASH
};
enum ld_arg {
NO_ARG,
REQ_ARG,
OPT_ARG
};
enum ld_key {
KEY_ACCEPT_UNKNOWN = 0x10000,
KEY_ALLOW_SHLIB_UNDEF,
KEY_ASSERT,
KEY_AS_NEEDED,
KEY_BUILD_ID,
KEY_CHECK_SECTIONS,
KEY_CREF,
KEY_DEFSYM,
KEY_DEMANGLE,
KEY_DISABLE_NEW_DTAGS,
KEY_DYNAMIC,
KEY_EB,
KEY_EL,
KEY_EH_FRAME_HDR,
KEY_ENABLE_NEW_DTAGS,
KEY_ERR_UNRESOLVE_SYM,
KEY_FATAL_WARNINGS,
KEY_FINI,
KEY_GC_SECTIONS,
KEY_GROUP,
KEY_HASH_STYLE,
KEY_HELP,
KEY_INIT,
KEY_MAP,
KEY_NO_AS_NEEDED,
KEY_NO_CHECK_SECTIONS,
KEY_NO_DEFINE_COMMON,
KEY_NO_DEMANGLE,
KEY_NO_GC_SECTIONS,
KEY_NO_KEEP_MEMORY,
KEY_NO_OMAGIC,
KEY_NO_PRINT_GC_SECTIONS,
KEY_NO_SHLIB_UNDEF,
KEY_NO_STDLIB,
KEY_NO_UNDEF_VERSION,
KEY_NO_UNKNOWN,
KEY_NO_WHOLE_ARCHIVE,
KEY_NO_WARN_MISMATCH,
KEY_RPATH,
KEY_RPATH_LINK,
KEY_RUNPATH,
KEY_SECTION_START,
KEY_OFORMAT,
KEY_PIE,
KEY_PRINT_GC_SECTIONS,
KEY_QMAGIC,
KEY_QY,
KEY_RELAX,
KEY_RETAIN_SYM_FILE,
KEY_SHARED,
KEY_SORT_COMMON,
KEY_SPLIT_BY_FILE,
KEY_SPLIT_BY_RELOC,
KEY_STATIC,
KEY_STATS,
KEY_SYMBOLIC,
KEY_SYMBOLIC_FUNC,
KEY_TBSS,
KEY_TDATA,
KEY_TTEXT,
KEY_TRADITIONAL_FORMAT,
KEY_UNRESOLVED_SYMBOLS,
KEY_UNIQUE,
KEY_UR,
KEY_VERSION,
KEY_VERSION_SCRIPT,
KEY_WARN_COMMON,
KEY_WARN_CONSTRUCTORS,
KEY_WARN_MULTIPLE_GP,
KEY_WARN_ONCE,
KEY_WARN_SECTION_ALIGN,
KEY_WARN_SHARED_TEXTREL,
KEY_WARN_UNRESOLVE_SYM,
KEY_WHOLE_ARCHIVE,
KEY_WRAP,
KEY_Z_DEFAULT_EXTRACT,
KEY_Z_DEFS,
KEY_Z_EXEC_STACK,
KEY_Z_IGNORE,
KEY_Z_INIT_FIRST,
KEY_Z_LAZYLOAD,
KEY_Z_MULDEFS,
KEY_Z_NOW,
KEY_Z_NO_DEFAULT_LIB,
KEY_Z_NO_DEFS,
KEY_Z_NO_DELETE,
KEY_Z_NO_DLOPEN,
KEY_Z_NO_EXEC_STACK,
KEY_Z_NO_LAZYLOAD,
KEY_Z_ORIGIN,
KEY_Z_RECORD,
KEY_Z_SYSTEM_LIBRARY,
KEY_Z_WEAK_EXTRACT,
KEY_FILE = 0x10000000,
};
struct ld_option {
const char *lo_long;
int lo_key;
enum ld_dash lo_dash;
enum ld_arg lo_arg;
};
enum ld_wildcard_sort {
LWS_NONE,
LWS_NAME,
LWS_ALIGN,
LWS_NAME_ALIGN,
LWS_ALIGN_NAME,
};
struct ld_wildcard {
char *lw_name; /* wildcard */
enum ld_wildcard_sort lw_sort; /* sort mode */
};
void ld_options_parse(struct ld*, int, char **);
struct ld_wildcard *ld_wildcard_alloc(struct ld *);
void ld_wildcard_free(void *);

1154
ld/ld_output.c Normal file

File diff suppressed because it is too large Load Diff

165
ld/ld_output.h Normal file
View File

@ -0,0 +1,165 @@
/*-
* Copyright (c) 2011-2013 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: ld_output.h 2959 2013-08-25 03:12:47Z kaiwang27 $
*/
enum ld_output_element_type {
OET_ASSERT,
OET_ASSIGN,
OET_DATA,
OET_ENTRY,
OET_INPUT_SECTION_LIST,
OET_KEYWORD,
OET_OUTPUT_SECTION,
OET_OVERLAY,
OET_DATA_BUFFER,
OET_SYMTAB,
OET_STRTAB
};
struct ld_output_element {
enum ld_output_element_type oe_type; /* output element type */
uint64_t oe_off; /* output element offset */
void *oe_entry; /* output element */
void *oe_islist; /* input section list */
unsigned char oe_insec; /* element inside SECTIONS */
STAILQ_ENTRY(ld_output_element) oe_next; /* next element */
};
STAILQ_HEAD(ld_output_element_head, ld_output_element);
struct ld_output_data_buffer {
uint8_t *odb_buf; /* point to data */
uint64_t odb_size; /* buffer size */
uint64_t odb_off; /* relative offset in output section */
uint64_t odb_align; /* buffer alignment */
uint64_t odb_type; /* buffer data type */
};
struct ld_reloc_entry_head;
struct ld_symbol;
struct ld_output_section {
Elf_Scn *os_scn; /* output section descriptor */
char *os_name; /* output section name */
uint64_t os_addr; /* output section vma */
uint64_t os_lma; /* output section lma */
uint64_t os_off; /* output section offset */
uint64_t os_size; /* output section size */
uint64_t os_align; /* output section alignment */
uint64_t os_flags; /* output section flags */
uint64_t os_type; /* output section type */
uint64_t os_entsize; /* output seciton entry size */
uint64_t os_info_val; /* output section info */
unsigned char os_empty; /* output section is empty */
unsigned char os_dynrel; /* contains dynamic relocations */
unsigned char os_pltrel; /* contains PLT relocations */
unsigned char os_rel; /* contains normal relocations */
unsigned char os_entsize_set; /* entsize is set */
char *os_link; /* link to other output section */
struct ld_symbol *os_secsym; /* assoicated STT_SECTION symbol */
struct ld_output_section *os_info; /* info refer to other section */
struct ld_output_section *os_r; /* relocation section */
struct ld_script_sections_output *os_ldso;
/* output section descriptor */
struct ld_output_element *os_pe; /* parent element */
struct ld_output_element_head os_e; /* list of child elements */
struct ld_reloc_entry_head *os_reloc; /* list of relocations */
uint64_t os_num_reloc; /* number of relocations */
STAILQ_ENTRY(ld_output_section) os_next; /* next output section */
UT_hash_handle hh; /* hash handle */
};
STAILQ_HEAD(ld_output_section_head, ld_output_section);
struct ld_symver_verneed_head;
struct ld_output {
int lo_fd; /* output file descriptor */
Elf *lo_elf; /* output ELF descriptor */
int lo_ec; /* output object elf class */
int lo_endian; /* outout object endianess */
int lo_osabi; /* output object osabi */
int lo_soname_nameindex; /* string index for DT_SONAME */
int lo_rpath_nameindex; /* string index for DT_RPATH */
unsigned lo_phdr_num; /* num of phdrs */
unsigned lo_phdr_note; /* create PT_NOTE */
unsigned lo_dso_needed; /* num of DSO referenced */
unsigned lo_version_index; /* current symver index */
unsigned lo_verdef_num; /* num of verdef entries */
unsigned lo_verneed_num; /* num of verneed entries */
unsigned lo_rel_plt_type; /* type of PLT relocation */
unsigned lo_rel_dyn_type; /* type of dynamic relocation */
unsigned lo_fde_num; /* num of FDE in .eh_frame */
uint64_t lo_shoff; /* section header table offset */
uint64_t lo_tls_size; /* TLS segment size */
uint64_t lo_tls_align; /* TLS segment align */
uint64_t lo_tls_addr; /* TLS segment VMA */
size_t lo_symtab_shndx; /* .symtab section index */
UT_array *lo_dso_nameindex; /* array of DSO name indices */
struct ld_symver_verneed_head *lo_vnlist; /* Verneed list */
struct ld_output_element_head lo_oelist; /* output element list */
struct ld_output_section_head lo_oslist; /* output section list */
struct ld_output_section *lo_ostbl; /* output section hash table */
struct ld_output_section *lo_interp; /* .interp section. */
struct ld_output_section *lo_init; /* .init section */
struct ld_output_section *lo_fini; /* .fini section */
struct ld_output_section *lo_dynamic; /* .dynamic section. */
struct ld_output_section *lo_dynsym; /* .dynsym section. */
struct ld_output_section *lo_dynstr; /* .dynstr section. */
struct ld_output_section *lo_hash; /* .hash section. */
struct ld_output_section *lo_verdef; /* .gnu.version.d section */
struct ld_output_section *lo_verneed; /* .gnu.version.r section */
struct ld_output_section *lo_versym; /* .gnu.version section */
struct ld_output_section *lo_gotplt; /* GOT(for PLT) section */
struct ld_output_section *lo_plt; /* PLT section */
struct ld_output_section *lo_rel_plt; /* PLT relocation section */
struct ld_output_section *lo_rel_dyn; /* Dynamic relocation section */
struct ld_output_section *lo_ehframe_hdr; /* .eh_frame_hdr section */
struct ld_output_data_buffer *lo_dynamic_odb; /* .dynamic buffer */
struct ld_output_data_buffer *lo_got_odb; /* GOT section data */
struct ld_output_data_buffer *lo_plt_odb; /* PLT section data */
struct ld_output_data_buffer *lo_rel_plt_odb; /* PLT reloc data */
struct ld_output_data_buffer *lo_rel_dyn_odb; /* dynamic reloc data */
};
struct ld_output_section *ld_output_alloc_section(struct ld *, const char *,
struct ld_output_section *, struct ld_output_section *);
void ld_output_create(struct ld *);
struct ld_output_element *ld_output_create_element(struct ld *,
struct ld_output_element_head *, enum ld_output_element_type, void *,
struct ld_output_element *);
struct ld_output_element *ld_output_create_section_element(struct ld *,
struct ld_output_section *, enum ld_output_element_type, void *,
struct ld_output_element *);
void ld_output_create_elf_sections(struct ld *);
void ld_output_create_string_table_section(struct ld *, const char *,
struct ld_strtab *, Elf_Scn *);
void ld_output_emit_gnu_stack_section(struct ld *);
void ld_output_format(struct ld *, char *, char *, char *);
void ld_output_early_init(struct ld *);
void ld_output_init(struct ld *);
void ld_output_write(struct ld *);

295
ld/ld_path.c Normal file
View File

@ -0,0 +1,295 @@
/*-
* Copyright (c) 2010-2013 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: ld_path.c 2930 2013-03-17 22:54:26Z kaiwang27 $
*/
#include "ld.h"
#include "ld_file.h"
#include "ld_path.h"
static char *_search_file(struct ld *ld, const char *path, const char *file);
static char *
_search_file(struct ld *ld, const char *path, const char *file)
{
struct dirent *dp;
DIR *dirp;
char *fp;
assert(path != NULL && file != NULL);
if ((dirp = opendir(path)) == NULL) {
ld_warn(ld, "opendir failed: %s", strerror(errno));
return (NULL);
}
fp = NULL;
while ((dp = readdir(dirp)) != NULL) {
if (!strcmp(dp->d_name, file)) {
if ((fp = malloc(PATH_MAX + 1)) == NULL)
ld_fatal_std(ld, "malloc");
fp[0] = '\0';
snprintf(fp, PATH_MAX + 1, "%s/%s", path, dp->d_name);
break;
}
}
(void) closedir(dirp);
return (fp);
}
void
ld_path_add(struct ld *ld, char *path, enum ld_path_type lpt)
{
struct ld_state *ls;
struct ld_path *lp;
assert(ld != NULL && path != NULL);
ls = &ld->ld_state;
if ((lp = calloc(1, sizeof(*lp))) == NULL)
ld_fatal_std(ld, "calloc");
if ((lp->lp_path = strdup(path)) == NULL)
ld_fatal_std(ld, "strdup");
switch (lpt) {
case LPT_L:
STAILQ_INSERT_TAIL(&ls->ls_lplist, lp, lp_next);
break;
case LPT_RPATH:
STAILQ_INSERT_TAIL(&ls->ls_rplist, lp, lp_next);
break;
case LPT_RPATH_LINK:
STAILQ_INSERT_TAIL(&ls->ls_rllist, lp, lp_next);
break;
default:
ld_fatal(ld, "Internal: invalid path type %d", lpt);
break;
}
}
void
ld_path_add_multiple(struct ld *ld, char *str, enum ld_path_type lpt)
{
char *p;
while ((p = strsep(&str, ":")) != NULL) {
if (*p != '\0')
ld_path_add(ld, p, lpt);
}
}
void
ld_path_cleanup(struct ld *ld)
{
struct ld_state *ls;
struct ld_path *lp, *_lp;
ls = &ld->ld_state;
STAILQ_FOREACH_SAFE(lp, &ls->ls_lplist, lp_next, _lp) {
STAILQ_REMOVE(&ls->ls_lplist, lp, ld_path, lp_next);
free(lp->lp_path);
free(lp);
}
STAILQ_FOREACH_SAFE(lp, &ls->ls_rplist, lp_next, _lp) {
STAILQ_REMOVE(&ls->ls_rplist, lp, ld_path, lp_next);
free(lp->lp_path);
free(lp);
}
STAILQ_FOREACH_SAFE(lp, &ls->ls_rllist, lp_next, _lp) {
STAILQ_REMOVE(&ls->ls_rllist, lp, ld_path, lp_next);
free(lp->lp_path);
free(lp);
}
}
char *
ld_path_join_rpath(struct ld *ld)
{
struct ld_state *ls;
struct ld_path *lp;
char *s;
int len;
ls = &ld->ld_state;
if (STAILQ_EMPTY(&ls->ls_rplist))
return (NULL);
len = 0;
STAILQ_FOREACH(lp, &ls->ls_rplist, lp_next)
len += strlen(lp->lp_path) + 1;
if ((s = malloc(len)) == NULL)
ld_fatal_std(ld, "malloc");
STAILQ_FOREACH(lp, &ls->ls_rplist, lp_next) {
strcat(s, lp->lp_path);
if (lp != STAILQ_LAST(&ls->ls_rplist, ld_path, lp_next))
strcat(s, ":");
}
return (s);
}
void
ld_path_search_file(struct ld *ld, struct ld_file *lf)
{
struct ld_state *ls;
struct ld_path *lp;
char *fp;
int found;
assert(lf != NULL);
ls = &ld->ld_state;
found = 0;
STAILQ_FOREACH(lp, &ls->ls_lplist, lp_next) {
if ((fp = _search_file(ld, lp->lp_path, lf->lf_name)) !=
NULL) {
free(lf->lf_name);
lf->lf_name = fp;
found = 1;
break;
}
}
if (!found)
ld_fatal(ld, "cannot find %s", lf->lf_name);
}
void
ld_path_search_library(struct ld *ld, const char *name)
{
struct ld_state *ls;
struct ld_path *lp;
struct dirent *dp;
DIR *dirp;
char fp[PATH_MAX + 1], sfp[PATH_MAX + 1];
size_t len;
int found;
assert(ld != NULL && name != NULL);
ls = &ld->ld_state;
len = strlen(name);
found = 0;
STAILQ_FOREACH(lp, &ls->ls_lplist, lp_next) {
assert(lp->lp_path != NULL);
if ((dirp = opendir(lp->lp_path)) == NULL) {
ld_warn(ld, "opendir failed: %s", strerror(errno));
continue;
}
fp[0] = sfp[0] = '\0';
while ((dp = readdir(dirp)) != NULL) {
if (strncmp(dp->d_name, "lib", 3))
continue;
if (strncmp(name, &dp->d_name[3], len))
continue;
if (ls->ls_static == 0 &&
!strcmp(&dp->d_name[len + 3], ".so")) {
snprintf(fp, sizeof(fp), "%s/%s", lp->lp_path,
dp->d_name);
ld_file_add(ld, fp, LFT_DSO);
(void) closedir(dirp);
found = 1;
goto done;
} else if (*sfp == '\0' &&
!strcmp(&dp->d_name[len + 3], ".a")) {
snprintf(sfp, sizeof(sfp), "%s/%s", lp->lp_path,
dp->d_name);
if (ls->ls_static == 1) {
ld_file_add(ld, sfp, LFT_ARCHIVE);
(void) closedir(dirp);
found = 1;
goto done;
}
}
}
(void) closedir(dirp);
}
done:
if (!found) {
if (ls->ls_static == 0 && *sfp != '\0') {
ld_file_add(ld, sfp, LFT_ARCHIVE);
} else
ld_fatal(ld, "cannot find -l%s", name);
}
}
void
ld_path_search_dso_needed(struct ld *ld, struct ld_file *lf, const char *name)
{
struct ld_state *ls;
struct ld_path *lp;
struct ld_file *_lf;
char *fp;
ls = &ld->ld_state;
/*
* First check if we've seen this shared library or if it's
* already listed in the input file list.
*/
TAILQ_FOREACH(_lf, &ld->ld_lflist, lf_next) {
if (!strcmp(_lf->lf_name, name) ||
!strcmp(basename(_lf->lf_name), name))
return;
}
/* Search -rpath-link directories. */
STAILQ_FOREACH(lp, &ls->ls_rllist, lp_next) {
if ((fp = _search_file(ld, lp->lp_path, name)) != NULL)
goto done;
}
/* Search -rpath directories. */
STAILQ_FOREACH(lp, &ls->ls_rplist, lp_next) {
if ((fp = _search_file(ld, lp->lp_path, name)) != NULL)
goto done;
}
/* TODO: search additional directories and environment variables. */
/* Search /lib and /usr/lib. */
if ((fp = _search_file(ld, "/lib", name)) != NULL)
goto done;
if ((fp = _search_file(ld, "/usr/lib", name)) != NULL)
goto done;
/* Not found. */
ld_warn(ld, "cannot find needed shared library: %s", name);
return;
done:
ld_file_add_after(ld, fp, LFT_DSO, lf);
free(fp);
}

46
ld/ld_path.h Normal file
View File

@ -0,0 +1,46 @@
/*-
* Copyright (c) 2010-2013 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: ld_path.h 2930 2013-03-17 22:54:26Z kaiwang27 $
*/
enum ld_path_type {
LPT_L,
LPT_RPATH,
LPT_RPATH_LINK,
};
struct ld_path {
char *lp_path;
STAILQ_ENTRY(ld_path) lp_next;
};
void ld_path_add(struct ld *, char *, enum ld_path_type);
void ld_path_add_multiple(struct ld *, char *, enum ld_path_type);
void ld_path_cleanup(struct ld *);
char *ld_path_join_rpath(struct ld *);
void ld_path_search_file(struct ld *, struct ld_file *);
void ld_path_search_library(struct ld *, const char *);
void ld_path_search_dso_needed(struct ld *, struct ld_file *, const char *);

Some files were not shown because too many files have changed in this diff Show More