Vendor import of lld trunk r233088:

https://llvm.org/svn/llvm-project/lld/trunk@233088
This commit is contained in:
Dimitry Andric 2015-03-24 21:31:36 +00:00
commit fb911942f1
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/vendor/lld/dist/; revision=280461
svn path=/vendor/lld/lld-trunk-r233088/; revision=280462; tag=vendor/lld/lld-trunk-r233088
1131 changed files with 106976 additions and 0 deletions

4
.arcconfig Normal file
View file

@ -0,0 +1,4 @@
{
"project_id" : "lld",
"conduit_uri" : "http://reviews.llvm.org/"
}

1
.clang-format Normal file
View file

@ -0,0 +1 @@
BasedOnStyle: LLVM

24
.gitignore vendored Normal file
View file

@ -0,0 +1,24 @@
#==============================================================================#
# This file specifies intentionally untracked files that git should ignore.
# See: http://www.kernel.org/pub/software/scm/git/docs/gitignore.html
#==============================================================================#
#==============================================================================#
# File extensions to be ignored anywhere in the tree.
#==============================================================================#
# Temp files created by most text editors.
*~
# Merge files created by git.
*.orig
# Byte compiled python modules.
*.pyc
# vim swap files
.*.swp
# Mac OS X Finder layout info
.DS_Store
#==============================================================================#
# Directories to be ignored.
#==============================================================================#
# Sphinx build files.
docs/_build

98
CMakeLists.txt Normal file
View file

@ -0,0 +1,98 @@
set(LLD_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(LLD_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
# Compute the LLD version from the LLVM version.
string(REGEX MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?" LLD_VERSION
${PACKAGE_VERSION})
message(STATUS "LLD version: ${LLD_VERSION}")
string(REGEX REPLACE "([0-9]+)\\.[0-9]+(\\.[0-9]+)?" "\\1" LLD_VERSION_MAJOR
${LLD_VERSION})
string(REGEX REPLACE "[0-9]+\\.([0-9]+)(\\.[0-9]+)?" "\\1" LLD_VERSION_MINOR
${LLD_VERSION})
# Determine LLD revision and repository.
# TODO: Figure out a way to get the revision and the repository on windows.
if ( NOT CMAKE_SYSTEM_NAME MATCHES "Windows" )
execute_process(COMMAND ${CMAKE_SOURCE_DIR}/utils/GetSourceVersion ${LLD_SOURCE_DIR}
OUTPUT_VARIABLE LLD_REVISION)
execute_process(COMMAND ${CMAKE_SOURCE_DIR}/utils/GetRepositoryPath ${LLD_SOURCE_DIR}
OUTPUT_VARIABLE LLD_REPOSITORY)
if ( LLD_REPOSITORY )
# Replace newline characters with spaces
string(REGEX REPLACE "(\r?\n)+" " " LLD_REPOSITORY ${LLD_REPOSITORY})
# Remove leading spaces
STRING(REGEX REPLACE "^[ \t\r\n]+" "" LLD_REPOSITORY "${LLD_REPOSITORY}" )
# Remove trailing spaces
string(REGEX REPLACE "(\ )+$" "" LLD_REPOSITORY ${LLD_REPOSITORY})
endif()
if ( LLD_REVISION )
# Replace newline characters with spaces
string(REGEX REPLACE "(\r?\n)+" " " LLD_REVISION ${LLD_REVISION})
# Remove leading spaces
STRING(REGEX REPLACE "^[ \t\r\n]+" "" LLD_REVISION "${LLD_REVISION}" )
# Remove trailing spaces
string(REGEX REPLACE "(\ )+$" "" LLD_REVISION ${LLD_REVISION})
endif()
endif ()
# Configure the Version.inc file.
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/include/lld/Config/Version.inc.in
${CMAKE_CURRENT_BINARY_DIR}/include/lld/Config/Version.inc)
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
message(FATAL_ERROR "In-source builds are not allowed. CMake would overwrite "
"the makefiles distributed with LLVM. Please create a directory and run cmake "
"from there, passing the path to this source directory as the last argument. "
"This process created the file `CMakeCache.txt' and the directory "
"`CMakeFiles'. Please delete them.")
endif()
list (APPEND CMAKE_MODULE_PATH "${LLD_SOURCE_DIR}/cmake/modules")
option(LLD_USE_VTUNE
"Enable VTune user task tracking."
OFF)
if (LLD_USE_VTUNE)
find_package(VTune)
if (VTUNE_FOUND)
include_directories(${VTune_INCLUDE_DIRS})
list(APPEND LLVM_COMMON_LIBS ${VTune_LIBRARIES})
add_definitions(-DLLD_HAS_VTUNE)
endif()
endif()
if (MSVC)
add_definitions(-wd4530) # Suppress 'warning C4530: C++ exception handler used, but unwind semantics are not enabled.'
add_definitions(-wd4062) # Suppress 'warning C4062: enumerator X in switch of enum Y is not handled' from system header.
endif()
include_directories(BEFORE
${CMAKE_CURRENT_BINARY_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/include
)
if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
install(DIRECTORY include/
DESTINATION include
FILES_MATCHING
PATTERN "*.h"
PATTERN ".svn" EXCLUDE
)
endif()
add_subdirectory(lib)
add_subdirectory(tools)
add_subdirectory(test)
if (LLVM_INCLUDE_TESTS)
add_subdirectory(unittests)
endif()
add_subdirectory(docs)

62
LICENSE.TXT Normal file
View file

@ -0,0 +1,62 @@
==============================================================================
lld License
==============================================================================
University of Illinois/NCSA
Open Source License
Copyright (c) 2011-2015 by the contributors listed in CREDITS.TXT
All rights reserved.
Developed by:
LLVM Team
University of Illinois at Urbana-Champaign
http://llvm.org
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal with
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimers.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimers in the
documentation and/or other materials provided with the distribution.
* Neither the names of the LLVM Team, University of Illinois at
Urbana-Champaign, nor the names of its contributors may be used to
endorse or promote products derived from this Software without specific
prior written permission.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
SOFTWARE.
==============================================================================
The lld software contains code written by third parties. Such software will
have its own individual LICENSE.TXT file in the directory in which it appears.
This file will describe the copyrights, license, and restrictions which apply
to that code.
The disclaimer of warranty in the University of Illinois Open Source License
applies to all code in the lld Distribution, and nothing in any of the
other licenses gives permission to use the names of the LLVM Team or the
University of Illinois to endorse or promote products derived from this
Software.
The following pieces of software have additional or alternate copyrights,
licenses, and/or restrictions:
Program Directory
------- ---------
<none yet>

86
Makefile Normal file
View file

@ -0,0 +1,86 @@
##===- Makefile --------------------------------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
# If LLD_LEVEL is not set, then we are the top-level Makefile. Otherwise, we
# are being included from a subdirectory makefile.
ifndef LLD_LEVEL
IS_TOP_LEVEL := 1
LLD_LEVEL := .
DIRS := include lib tools unittests
PARALLEL_DIRS :=
endif
ifeq ($(MAKECMDGOALS),libs-only)
DIRS := $(filter-out tools docs, $(DIRS))
OPTIONAL_DIRS :=
endif
ifeq ($(BUILD_LLD_ONLY),YES)
DIRS := $(filter-out docs unittests, $(DIRS))
OPTIONAL_DIRS :=
endif
###
# Common Makefile code, shared by all lld Makefiles.
# Set LLVM source root level.
LEVEL := $(LLD_LEVEL)/../..
# Include LLVM common makefile.
include $(LEVEL)/Makefile.common
ifneq ($(ENABLE_DOCS),1)
DIRS := $(filter-out docs, $(DIRS))
endif
CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/include
CPP.Flags += -I$(PROJ_OBJ_DIR)/$(LLD_LEVEL)/include
###
# lld Top Level specific stuff.
ifeq ($(IS_TOP_LEVEL),1)
ifneq ($(PROJ_SRC_ROOT),$(PROJ_OBJ_ROOT))
$(RecursiveTargets)::
$(Verb) for dir in test unittests; do \
if [ -f $(PROJ_SRC_DIR)/$${dir}/Makefile ] && [ ! -f $${dir}/Makefile ]; then \
$(MKDIR) $${dir}; \
$(CP) $(PROJ_SRC_DIR)/$${dir}/Makefile $${dir}/Makefile; \
fi \
done
endif
test::
@ $(MAKE) -C test
report::
@ $(MAKE) -C test report
clean::
@ $(MAKE) -C test clean
libs-only: all
tags::
$(Verb) etags `find . -type f -name '*.h' -or -name '*.cpp' | \
grep -v /lib/Headers | grep -v /test/`
cscope.files:
find tools lib include -name '*.cpp' \
-or -name '*.def' \
-or -name '*.td' \
-or -name '*.h' > cscope.files
.PHONY: test report clean cscope.files
endif

10
README.md Normal file
View file

@ -0,0 +1,10 @@
LLVM Linker (lld)
==============================
This directory and its subdirectories contain source code for the LLVM Linker, a
modular cross platform linker which is built as part of the LLVM compiler
infrastructure project.
lld is open source software. You may freely distribute it under the terms of
the license agreement found in LICENSE.txt.

View file

@ -0,0 +1,31 @@
# - Find VTune ittnotify.
# Defines:
# VTune_FOUND
# VTune_INCLUDE_DIRS
# VTune_LIBRARIES
set(dirs
"$ENV{VTUNE_AMPLIFIER_XE_2013_DIR}/"
"C:/Program Files (x86)/Intel/VTune Amplifier XE 2013/"
"$ENV{VTUNE_AMPLIFIER_XE_2011_DIR}/"
"C:/Program Files (x86)/Intel/VTune Amplifier XE 2011/"
)
find_path(VTune_INCLUDE_DIRS ittnotify.h
PATHS ${dirs}
PATH_SUFFIXES include)
if (CMAKE_SIZEOF_VOID_P MATCHES "8")
set(vtune_lib_dir lib64)
else()
set(vtune_lib_dir lib32)
endif()
find_library(VTune_LIBRARIES libittnotify
HINTS "${VTune_INCLUDE_DIRS}/.."
PATHS ${dirs}
PATH_SUFFIXES ${vtune_lib_dir})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
VTune DEFAULT_MSG VTune_LIBRARIES VTune_INCLUDE_DIRS)

9
docs/C++11.rst Normal file
View file

@ -0,0 +1,9 @@
C++11
=====
Originally, LLD was developed in C++11 unlike the rest of LLVM. Now, all of
LLVM, LLD, and Clang are developed using C++11. See the `LLVM Coding
Standards`_ for details on the precise subset of C++11 supported by the various
host compilers.
.. _LLVM Coding Standards: http://llvm.org/docs/CodingStandards.html

8
docs/CMakeLists.txt Normal file
View file

@ -0,0 +1,8 @@
if (LLVM_ENABLE_SPHINX)
if (SPHINX_FOUND)
include(AddSphinxTarget)
if (${SPHINX_OUTPUT_HTML})
add_sphinx_target(html lld)
endif()
endif()
endif()

79
docs/Driver.rst Normal file
View file

@ -0,0 +1,79 @@
======
Driver
======
.. contents::
:local:
Introduction
============
This document describes the lld driver. The purpose of this document is to
describe both the motivation and design goals for the driver, as well as details
of the internal implementation.
Overview
========
The lld driver is designed to support a number of different command line
interfaces. The main interfaces we plan to support are binutils' ld, Apple's
ld, and Microsoft's link.exe.
Flavors
-------
Each of these different interfaces is referred to as a flavor. There is also an
extra flavor "core" which is used to exercise the core functionality of the
linker it the test suite.
* gnu
* darwin
* link
* core
Selecting a Flavor
^^^^^^^^^^^^^^^^^^
There are two different ways to tell lld which flavor to be. They are checked in
order, so the second overrides the first. The first is to symlink :program:`lld`
as :program:`lld-{flavor}` or just :program:`{flavor}`. You can also specify
it as the first command line argument using ``-flavor``::
$ lld -flavor gnu
There is a shortcut for ``-flavor core`` as ``-core``.
Adding an Option to an existing Flavor
======================================
#. Add the option to the desired :file:`lib/Driver/{flavor}Options.td`.
#. Add to :cpp:class:`lld::FlavorLinkingContext` a getter and setter method
for the option.
#. Modify :cpp:func:`lld::FlavorDriver::parse` in :file:
`lib/Driver/{Flavor}Driver.cpp` to call the targetInfo setter
for corresponding to the option.
#. Modify {Flavor}Reader and {Flavor}Writer to use the new targtInfo option.
Adding a Flavor
===============
#. Add an entry for the flavor in :file:`include/lld/Driver/Driver.h` to
:cpp:class:`lld::UniversalDriver::Flavor`.
#. Add an entry in :file:`lib/Driver/UniversalDriver.cpp` to
:cpp:func:`lld::Driver::strToFlavor` and
:cpp:func:`lld::UniversalDriver::link`.
This allows the flavor to be selected via symlink and :option:`-flavor`.
#. Add a tablegen file called :file:`lib/Driver/{flavor}Options.td` that
describes the options. If the options are a superset of another driver, that
driver's td file can simply be included. The :file:`{flavor}Options.td` file
must also be added to :file:`lib/Driver/CMakeLists.txt`.
#. Add a ``{flavor}Driver`` as a subclass of :cpp:class:`lld::Driver`
in :file:`lib/Driver/{flavor}Driver.cpp`.

155
docs/Makefile Normal file
View file

@ -0,0 +1,155 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
all: html
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/lld.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/lld.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/lld"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/lld"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."

12
docs/README.txt Normal file
View file

@ -0,0 +1,12 @@
lld Documentation
=================
The lld documentation is written using the Sphinx documentation generator. It is
currently tested with Sphinx 1.1.3.
We currently use the 'nature' theme and a Beaker inspired structure.
To rebuild documents into html:
[/lld/docs]> make html

172
docs/Readers.rst Normal file
View file

@ -0,0 +1,172 @@
.. _Readers:
Developing lld Readers
======================
Introduction
------------
The purpose of a "Reader" is to take an object file in a particular format
and create an `lld::File`:cpp:class: (which is a graph of Atoms)
representing the object file. A Reader inherits from
`lld::Reader`:cpp:class: which lives in
:file:`include/lld/Core/Reader.h` and
:file:`lib/Core/Reader.cpp`.
The Reader infrastructure for an object format ``Foo`` requires the
following pieces in order to fit into lld:
:file:`include/lld/ReaderWriter/ReaderFoo.h`
.. cpp:class:: ReaderOptionsFoo : public ReaderOptions
This Options class is the only way to configure how the Reader will
parse any file into an `lld::Reader`:cpp:class: object. This class
should be declared in the `lld`:cpp:class: namespace.
.. cpp:function:: Reader *createReaderFoo(ReaderOptionsFoo &reader)
This factory function configures and create the Reader. This function
should be declared in the `lld`:cpp:class: namespace.
:file:`lib/ReaderWriter/Foo/ReaderFoo.cpp`
.. cpp:class:: ReaderFoo : public Reader
This is the concrete Reader class which can be called to parse
object files. It should be declared in an anonymous namespace or
if there is shared code with the `lld::WriterFoo`:cpp:class: you
can make a nested namespace (e.g. `lld::foo`:cpp:class:).
You may have noticed that :cpp:class:`ReaderFoo` is not declared in the
``.h`` file. An important design aspect of lld is that all Readers are
created *only* through an object-format-specific
:cpp:func:`createReaderFoo` factory function. The creation of the Reader is
parametrized through a :cpp:class:`ReaderOptionsFoo` class. This options
class is the one-and-only way to control how the Reader operates when
parsing an input file into an Atom graph. For instance, you may want the
Reader to only accept certain architectures. The options class can be
instantiated from command line options or be programmatically configured.
Where to start
--------------
The lld project already has a skeleton of source code for Readers for
``ELF``, ``PECOFF``, ``MachO``, and lld's native Atom graph format
(both binary ``Native`` and ``YAML`` representations). If your file format
is a variant of one of those, you should modify the existing Reader to
support your variant. This is done by customizing the Options
class for the Reader and making appropriate changes to the ``.cpp`` file to
interpret those options and act accordingly.
If your object file format is not a variant of any existing Reader, you'll need
to create a new Reader subclass with the organization described above.
Readers are factories
---------------------
The linker will usually only instantiate your Reader once. That one Reader will
have its loadFile() method called many times with different input files.
To support multithreaded linking, the Reader may be parsing multiple input
files in parallel. Therefore, there should be no parsing state in you Reader
object. Any parsing state should be in ivars of your File subclass or in
some temporary object.
The key method to implement in a reader is::
virtual error_code loadFile(LinkerInput &input,
std::vector<std::unique_ptr<File>> &result);
It takes a memory buffer (which contains the contents of the object file
being read) and returns an instantiated lld::File object which is
a collection of Atoms. The result is a vector of File pointers (instead of
simple a File pointer) because some file formats allow multiple object
"files" to be encoded in one file system file.
Memory Ownership
----------------
Atoms are always owned by their File object. During core linking when Atoms
are coalesced or stripped away, core linking does not delete them.
Core linking just removes those unused Atoms from its internal list.
The destructor of a File object is responsible for deleting all Atoms it
owns, and if ownership of the MemoryBuffer was passed to it, the File
destructor needs to delete that too.
Making Atoms
------------
The internal model of lld is purely Atom based. But most object files do not
have an explicit concept of Atoms, instead most have "sections". The way
to think of this is that a section is just a list of Atoms with common
attributes.
The first step in parsing section-based object files is to cleave each
section into a list of Atoms. The technique may vary by section type. For
code sections (e.g. .text), there are usually symbols at the start of each
function. Those symbol addresses are the points at which the section is
cleaved into discrete Atoms. Some file formats (like ELF) also include the
length of each symbol in the symbol table. Otherwise, the length of each
Atom is calculated to run to the start of the next symbol or the end of the
section.
Other sections types can be implicitly cleaved. For instance c-string literals
or unwind info (e.g. .eh_frame) can be cleaved by having the Reader look at
the content of the section. It is important to cleave sections into Atoms
to remove false dependencies. For instance the .eh_frame section often
has no symbols, but contains "pointers" to the functions for which it
has unwind info. If the .eh_frame section was not cleaved (but left as one
big Atom), there would always be a reference (from the eh_frame Atom) to
each function. So the linker would be unable to coalesce or dead stripped
away the function atoms.
The lld Atom model also requires that a reference to an undefined symbol be
modeled as a Reference to an UndefinedAtom. So the Reader also needs to
create an UndefinedAtom for each undefined symbol in the object file.
Once all Atoms have been created, the second step is to create References
(recall that Atoms are "nodes" and References are "edges"). Most References
are created by looking at the "relocation records" in the object file. If
a function contains a call to "malloc", there is usually a relocation record
specifying the address in the section and the symbol table index. Your
Reader will need to convert the address to an Atom and offset and the symbol
table index into a target Atom. If "malloc" is not defined in the object file,
the target Atom of the Reference will be an UndefinedAtom.
Performance
-----------
Once you have the above working to parse an object file into Atoms and
References, you'll want to look at performance. Some techniques that can
help performance are:
* Use llvm::BumpPtrAllocator or pre-allocate one big vector<Reference> and then
just have each atom point to its subrange of References in that vector.
This can be faster that allocating each Reference as separate object.
* Pre-scan the symbol table and determine how many atoms are in each section
then allocate space for all the Atom objects at once.
* Don't copy symbol names or section content to each Atom, instead use
StringRef and ArrayRef in each Atom to point to its name and content in the
MemoryBuffer.
Testing
-------
We are still working on infrastructure to test Readers. The issue is that
you don't want to check in binary files to the test suite. And the tools
for creating your object file from assembly source may not be available on
every OS.
We are investigating a way to use YAML to describe the section, symbols,
and content of a file. Then have some code which will write out an object
file from that YAML description.
Once that is in place, you can write test cases that contain section/symbols
YAML and is run through the linker to produce Atom/References based YAML which
is then run through FileCheck to verify the Atoms and References are as
expected.

BIN
docs/_static/favicon.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

4
docs/_templates/indexsidebar.html vendored Normal file
View file

@ -0,0 +1,4 @@
<h3>Bugs</h3>
<p>lld bugs should be reported at the
LLVM <a href="http://llvm.org/bugs">Bugzilla</a>.</p>

12
docs/_templates/layout.html vendored Normal file
View file

@ -0,0 +1,12 @@
{% extends "!layout.html" %}
{% block extrahead %}
<style type="text/css">
table.right { float: right; margin-left: 20px; }
table.right td { border: 1px solid #ccc; }
</style>
{% endblock %}
{% block rootrellink %}
<li><a href="{{ pathto('index') }}">lld Home</a>&nbsp;|&nbsp;</li>
{% endblock %}

254
docs/conf.py Normal file
View file

@ -0,0 +1,254 @@
# -*- coding: utf-8 -*-
#
# lld documentation build configuration file.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.intersphinx', 'sphinx.ext.todo']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'lld'
copyright = u'2011-2014, LLVM Project'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '3.2'
# The full version, including alpha/beta/rc tags.
release = '3.2'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
today_fmt = '%Y-%m-%d'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
show_authors = True
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'friendly'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'llvm-theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = ["."]
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# If given, this must be the name of an image file (path relative to the
# configuration directory) that is the favicon of the docs. Modern browsers use
# this as icon for tabs, windows and bookmarks. It should be a Windows-style
# icon file (.ico), which is 16x16 or 32x32 pixels large. Default: None. The
# image file will be copied to the _static directory of the output HTML, but
# only if the file does not already exist there.
html_favicon = '_static/favicon.ico'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
html_last_updated_fmt = '%Y-%m-%d'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
html_sidebars = {'index': 'indexsidebar.html'}
# Additional templates that should be rendered to pages, maps page names to
# template names.
# html_additional_pages = {'index': 'index.html'}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'llddoc'
# -- Options for LaTeX output --------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('contents', 'lld.tex', u'lld Documentation',
u'LLVM project', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('contents', 'lld', u'lld Documentation',
[u'LLVM project'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output ------------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('contents', 'lld', u'lld Documentation',
u'LLVM project', 'lld', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# FIXME: Define intersphinx configration.
intersphinx_mapping = {}
# -- Options for extensions ----------------------------------------------------
# Enable this if you want TODOs to show up in the generated documentation.
todo_include_todos = True

500
docs/design.rst Normal file
View file

@ -0,0 +1,500 @@
.. _design:
Linker Design
=============
Introduction
------------
lld is a new generation of linker. It is not "section" based like traditional
linkers which mostly just interlace sections from multiple object files into the
output file. Instead, lld is based on "Atoms". Traditional section based
linking work well for simple linking, but their model makes advanced linking
features difficult to implement. Features like dead code stripping, reordering
functions for locality, and C++ coalescing require the linker to work at a finer
grain.
An atom is an indivisible chunk of code or data. An atom has a set of
attributes, such as: name, scope, content-type, alignment, etc. An atom also
has a list of References. A Reference contains: a kind, an optional offset, an
optional addend, and an optional target atom.
The Atom model allows the linker to use standard graph theory models for linking
data structures. Each atom is a node, and each Reference is an edge. The
feature of dead code stripping is implemented by following edges to mark all
live atoms, and then delete the non-live atoms.
Atom Model
----------
An atom is an indivisible chunk of code or data. Typically each user written
function or global variable is an atom. In addition, the compiler may emit
other atoms, such as for literal c-strings or floating point constants, or for
runtime data structures like dwarf unwind info or pointers to initializers.
A simple "hello world" object file would be modeled like this:
.. image:: hello.png
There are three atoms: main, a proxy for printf, and an anonymous atom
containing the c-string literal "hello world". The Atom "main" has two
references. One is the call site for the call to printf, and the other is a
reference for the instruction that loads the address of the c-string literal.
There are only four different types of atoms:
* DefinedAtom
95% of all atoms. This is a chunk of code or data
* UndefinedAtom
This is a place holder in object files for a reference to some atom
outside the translation unit.During core linking it is usually replaced
by (coalesced into) another Atom.
* SharedLibraryAtom
If a required symbol name turns out to be defined in a dynamic shared
library (and not some object file). A SharedLibraryAtom is the
placeholder Atom used to represent that fact.
It is similar to an UndefinedAtom, but it also tracks information
about the associated shared library.
* AbsoluteAtom
This is for embedded support where some stuff is implemented in ROM at
some fixed address. This atom has no content. It is just an address
that the Writer needs to fix up any references to point to.
File Model
----------
The linker views the input files as basically containers of Atoms and
References, and just a few attributes of their own. The linker works with three
kinds of files: object files, static libraries, and dynamic shared libraries.
Each kind of file has reader object which presents the file in the model
expected by the linker.
Object File
~~~~~~~~~~~
An object file is just a container of atoms. When linking an object file, a
reader is instantiated which parses the object file and instantiates a set of
atoms representing all content in the .o file. The linker adds all those atoms
to a master graph.
Static Library (Archive)
~~~~~~~~~~~~~~~~~~~~~~~~
This is the traditional unix static archive which is just a collection of object
files with a "table of contents". When linking with a static library, by default
nothing is added to the master graph of atoms. Instead, if after merging all
atoms from object files into a master graph, if any "undefined" atoms are left
remaining in the master graph, the linker reads the table of contents for each
static library to see if any have the needed definitions. If so, the set of
atoms from the specified object file in the static library is added to the
master graph of atoms.
Dynamic Library (Shared Object)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Dynamic libraries are different than object files and static libraries in that
they don't directly add any content. Their purpose is to check at build time
that the remaining undefined references can be resolved at runtime, and provide
a list of dynamic libraries (SO_NEEDED) that will be needed at runtime. The way
this is modeled in the linker is that a dynamic library contributes no atoms to
the initial graph of atoms. Instead, (like static libraries) if there are
"undefined" atoms in the master graph of all atoms, then each dynamic library is
checked to see if exports the required symbol. If so, a "shared library" atom is
instantiated by the by the reader which the linker uses to replace the
"undefined" atom.
Linking Steps
-------------
Through the use of abstract Atoms, the core of linking is architecture
independent and file format independent. All command line parsing is factored
out into a separate "options" abstraction which enables the linker to be driven
with different command line sets.
The overall steps in linking are:
#. Command line processing
#. Parsing input files
#. Resolving
#. Passes/Optimizations
#. Generate output file
The Resolving and Passes steps are done purely on the master graph of atoms, so
they have no notion of file formats such as mach-o or ELF.
Input Files
~~~~~~~~~~~
Existing developer tools using different file formats for object files.
A goal of lld is to be file format independent. This is done
through a plug-in model for reading object files. The lld::Reader is the base
class for all object file readers. A Reader follows the factory method pattern.
A Reader instantiates an lld::File object (which is a graph of Atoms) from a
given object file (on disk or in-memory).
Every Reader subclass defines its own "options" class (for instance the mach-o
Reader defines the class ReaderOptionsMachO). This options class is the
one-and-only way to control how the Reader operates when parsing an input file
into an Atom graph. For instance, you may want the Reader to only accept
certain architectures. The options class can be instantiated from command
line options, or it can be subclassed and the ivars programmatically set.
ELF Section Groups
~~~~~~~~~~~~~~~~~~
Reference : `ELF Section Groups <http://mentorembedded.github.io/cxx-abi/abi/prop-72-comdat.html>`_
C++ has many situations where the compiler may need to emit code or data,
but may not be able to identify a unique compilation unit where it should be
emitted. The approach chosen by the C++ ABI group to deal with this problem, is
to allow the compiler to emit the required information in multiple compilation
units, in a form which allows the linker to remove all but one copy. This is
essentially the feature called COMDAT in several existing implementations.
The COMDAT sections in ELF are modeled by using '.group' sections in the input
files. Each '.group' section is associated with a signature. The '.group'
section has a list of members that are part of the the '.group' which the linker
selects to appear in the input file(Whichever .group section appeared first
in the link). References to any of the '.group' members can also appear from
outside the '.group'.
In lld the the '.group' sections with COMDAT are identified by contentType(
typeGroupComdat). The '.group' members are identified by using
**kindGroupChild** references.
The point to be noted here is the 'group child' members would need to be emitted
in the output file **iff** the group was selected by the resolver.
This is modeled in lld by removing the 'group child' members from the
definedAtom List.
Any reference to the group-child from **outside the group** is referenced using
a 'undefined' atom.
Resolving
~~~~~~~~~
The resolving step takes all the atoms' graphs from each object file and
combines them into one master object graph. Unfortunately, it is not as simple
as appending the atom list from each file into one big list. There are many
cases where atoms need to be coalesced. That is, two or more atoms need to be
coalesced into one atom. This is necessary to support: C language "tentative
definitions", C++ weak symbols for templates and inlines defined in headers,
replacing undefined atoms with actual definition atoms, and for merging copies
of constants like c-strings and floating point constants.
The linker support coalescing by-name and by-content. By-name is used for
tentative definitions and weak symbols. By-content is used for constant data
that can be merged.
The resolving process maintains some global linking "state", including a "symbol
table" which is a map from llvm::StringRef to lld::Atom*. With these data
structures, the linker iterates all atoms in all input files. For each atom, it
checks if the atom is named and has a global or hidden scope. If so, the atom
is added to the symbol table map. If there already is a matching atom in that
table, that means the current atom needs to be coalesced with the found atom, or
it is a multiple definition error.
When all initial input file atoms have been processed by the resolver, a scan is
made to see if there are any undefined atoms in the graph. If there are, the
linker scans all libraries (both static and dynamic) looking for definitions to
replace the undefined atoms. It is an error if any undefined atoms are left
remaining.
Dead code stripping (if requested) is done at the end of resolving. The linker
does a simple mark-and-sweep. It starts with "root" atoms (like "main" in a main
executable) and follows each references and marks each Atom that it visits as
"live". When done, all atoms not marked "live" are removed.
The result of the Resolving phase is the creation of an lld::File object. The
goal is that the lld::File model is **the** internal representation
throughout the linker. The file readers parse (mach-o, ELF, COFF) into an
lld::File. The file writers (mach-o, ELF, COFF) taken an lld::File and produce
their file kind, and every Pass only operates on an lld::File. This is not only
a simpler, consistent model, but it enables the state of the linker to be dumped
at any point in the link for testing purposes.
Passes
~~~~~~
The Passes step is an open ended set of routines that each get a change to
modify or enhance the current lld::File object. Some example Passes are:
* stub (PLT) generation
* GOT instantiation
* order_file optimization
* branch island generation
* branch shim generation
* Objective-C optimizations (Darwin specific)
* TLV instantiation (Darwin specific)
* DTrace probe processing (Darwin specific)
* compact unwind encoding (Darwin specific)
Some of these passes are specific to Darwin's runtime environments. But many of
the passes are applicable to any OS (such as generating branch island for out of
range branch instructions).
The general structure of a pass is to iterate through the atoms in the current
lld::File object, inspecting each atom and doing something. For instance, the
stub pass, looks for call sites to shared library atoms (e.g. call to printf).
It then instantiates a "stub" atom (PLT entry) and a "lazy pointer" atom for
each proxy atom needed, and these new atoms are added to the current lld::File
object. Next, all the noted call sites to shared library atoms have their
References altered to point to the stub atom instead of the shared library atom.
Generate Output File
~~~~~~~~~~~~~~~~~~~~
Once the passes are done, the output file writer is given current lld::File
object. The writer's job is to create the executable content file wrapper and
place the content of the atoms into it.
lld uses a plug-in model for writing output files. All concrete writers (e.g.
ELF, mach-o, etc) are subclasses of the lld::Writer class.
Unlike the Reader class which has just one method to instantiate an lld::File,
the Writer class has multiple methods. The crucial method is to generate the
output file, but there are also methods which allow the Writer to contribute
Atoms to the resolver and specify passes to run.
An example of contributing
atoms is that if the Writer knows a main executable is being linked and such
an executable requires a specially named entry point (e.g. "_main"), the Writer
can add an UndefinedAtom with that special name to the resolver. This will
cause the resolver to issue an error if that symbol is not defined.
Sometimes a Writer supports lazily created symbols, such as names for the start
of sections. To support this, the Writer can create a File object which vends
no initial atoms, but does lazily supply atoms by name as needed.
Every Writer subclass defines its own "options" class (for instance the mach-o
Writer defines the class WriterOptionsMachO). This options class is the
one-and-only way to control how the Writer operates when producing an output
file from an Atom graph. For instance, you may want the Writer to optimize
the output for certain OS versions, or strip local symbols, etc. The options
class can be instantiated from command line options, or it can be subclassed
and the ivars programmatically set.
lld::File representations
-------------------------
Just as LLVM has three representations of its IR model, lld has three
representations of its File/Atom/Reference model:
* In memory, abstract C++ classes (lld::Atom, lld::Reference, and lld::File).
* textual (in YAML)
* binary format ("native")
Binary File Format
~~~~~~~~~~~~~~~~~~
In theory, lld::File objects could be written to disk in an existing Object File
format standard (e.g. ELF). Instead we choose to define a new binary file
format. There are two main reasons for this: fidelity and performance. In order
for lld to work as a linker on all platforms, its internal model must be rich
enough to model all CPU and OS linking features. But if we choose an existing
Object File format as the lld binary format, that means an on going need to
retrofit each platform specific feature needed from alternate platforms into the
existing Object File format. Having our own "native" binary format side steps
that issue. We still need to be able to binary encode all the features, but
once the in-memory model can represent the feature, it is straight forward to
binary encode it.
The reason to use a binary file format at all, instead of a textual file format,
is speed. You want the binary format to be as fast as possible to read into the
in-memory model. Given that we control the in-memory model and the binary
format, the obvious way to make reading super fast it to make the file format be
basically just an array of atoms. The reader just mmaps in the file and looks
at the header to see how many atoms there are and instantiate that many atom
objects with the atom attribute information coming from that array. The trick
is designing this in a way that can be extended as the Atom mode evolves and new
attributes are added.
The native object file format starts with a header that lists how many "chunks"
are in the file. A chunk is an array of "ivar data". The native file reader
instantiates an array of Atom objects (with one large malloc call). Each atom
contains just a pointer to its vtable and a pointer to its ivar data. All
methods on lld::Atom are virtual, so all the method implementations return
values based on the ivar data to which it has a pointer. If a new linking
features is added which requires a change to the lld::Atom model, a new native
reader class (e.g. version 2) is defined which knows how to read the new feature
information from the new ivar data. The old reader class (e.g. version 1) is
updated to do its best to model (the lack of the new feature) given the old ivar
data in existing native object files.
With this model for the native file format, files can be read and turned
into the in-memory graph of lld::Atoms with just a few memory allocations.
And the format can easily adapt over time to new features.
The binary file format follows the ReaderWriter patterns used in lld. The lld
library comes with the classes: ReaderNative and WriterNative. So, switching
between file formats is as easy as switching which Reader subclass is used.
Textual representations in YAML
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In designing a textual format we want something easy for humans to read and easy
for the linker to parse. Since an atom has lots of attributes most of which are
usually just the default, we should define default values for every attribute so
that those can be omitted from the text representation. Here is the atoms for a
simple hello world program expressed in YAML::
target-triple: x86_64-apple-darwin11
atoms:
- name: _main
scope: global
type: code
content: [ 55, 48, 89, e5, 48, 8d, 3d, 00, 00, 00, 00, 30, c0, e8, 00, 00,
00, 00, 31, c0, 5d, c3 ]
fixups:
- offset: 07
kind: pcrel32
target: 2
- offset: 0E
kind: call32
target: _fprintf
- type: c-string
content: [ 73, 5A, 00 ]
...
The biggest use for the textual format will be writing test cases. Writing test
cases in C is problematic because the compiler may vary its output over time for
its own optimization reasons which my inadvertently disable or break the linker
feature trying to be tested. By writing test cases in the linkers own textual
format, we can exactly specify every attribute of every atom and thus target
specific linker logic.
The textual/YAML format follows the ReaderWriter patterns used in lld. The lld
library comes with the classes: ReaderYAML and WriterYAML.
Testing
-------
The lld project contains a test suite which is being built up as new code is
added to lld. All new lld functionality should have a tests added to the test
suite. The test suite is `lit <http://llvm.org/cmds/lit.html/>`_ driven. Each
test is a text file with comments telling lit how to run the test and check the
result To facilitate testing, the lld project builds a tool called lld-core.
This tool reads a YAML file (default from stdin), parses it into one or more
lld::File objects in memory and then feeds those lld::File objects to the
resolver phase. The output of the resolver is written as a native object file.
It is then read back in using the native object file reader and then pass to the
YAML writer. This round-about path means that all three representations
(in-memory, binary, and text) are exercised, and any new feature has to work in
all the representations to pass the test.
Resolver testing
~~~~~~~~~~~~~~~~
Basic testing is the "core linking" or resolving phase. That is where the
linker merges object files. All test cases are written in YAML. One feature of
YAML is that it allows multiple "documents" to be encoding in one YAML stream.
That means one text file can appear to the linker as multiple .o files - the
normal case for the linker.
Here is a simple example of a core linking test case. It checks that an
undefined atom from one file will be replaced by a definition from another
file::
# RUN: lld-core %s | FileCheck %s
#
# Test that undefined atoms are replaced with defined atoms.
#
---
atoms:
- name: foo
definition: undefined
---
atoms:
- name: foo
scope: global
type: code
...
# CHECK: name: foo
# CHECK: scope: global
# CHECK: type: code
# CHECK-NOT: name: foo
# CHECK: ...
Passes testing
~~~~~~~~~~~~~~
Since Passes just operate on an lld::File object, the lld-core tool has the
option to run a particular pass (after resolving). Thus, you can write a YAML
test case with carefully crafted input to exercise areas of a Pass and the check
the resulting lld::File object as represented in YAML.
Design Issues
-------------
There are a number of open issues in the design of lld. The plan is to wait and
make these design decisions when we need to.
Debug Info
~~~~~~~~~~
Currently, the lld model says nothing about debug info. But the most popular
debug format is DWARF and there is some impedance mismatch with the lld model
and DWARF. In lld there are just Atoms and only Atoms that need to be in a
special section at runtime have an associated section. Also, Atoms do not have
addresses. The way DWARF is spec'ed different parts of DWARF are supposed to go
into specially named sections and the DWARF references function code by address.
CPU and OS specific functionality
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Currently, lld has an abstract "Platform" that deals with any CPU or OS specific
differences in linking. We just keep adding virtual methods to the base
Platform class as we find linking areas that might need customization. At some
point we'll need to structure this better.
File Attributes
~~~~~~~~~~~~~~~
Currently, lld::File just has a path and a way to iterate its atoms. We will
need to add more attributes on a File. For example, some equivalent to the
target triple. There is also a number of cached or computed attributes that
could make various Passes more efficient. For instance, on Darwin there are a
number of Objective-C optimizations that can be done by a Pass. But it would
improve the plain C case if the Objective-C optimization Pass did not have to
scan all atoms looking for any Objective-C data structures. This could be done
if the lld::File object had an attribute that said if the file had any
Objective-C data in it. The Resolving phase would then be required to "merge"
that attribute as object files are added.

48
docs/development.rst Normal file
View file

@ -0,0 +1,48 @@
.. _development:
Development
===========
lld is developed as part of the `LLVM <http://llvm.org>`_ project.
Using C++11 in lld
------------------
:doc:`C++11`.
Creating a Reader
-----------------
See the :ref:`Creating a Reader <Readers>` guide.
Modifying the Driver
--------------------
See :doc:`Driver`.
Debugging
---------
You can run lld with ``-mllvm -debug`` command line options to enable debugging
printouts. If you want to enable debug information for some specific pass, you
can run it with ``-mllvm '-debug-only=<pass>'``, where pass is a name used in
the ``DEBUG_WITH_TYPE()`` macro.
Documentation
-------------
The project documentation is written in reStructuredText and generated using the
`Sphinx <http://sphinx.pocoo.org/>`_ documentation generator. For more
information on writing documentation for the project, see the
:ref:`sphinx_intro`.
.. toctree::
:hidden:
C++11
Readers
Driver

106
docs/getting_started.rst Normal file
View file

@ -0,0 +1,106 @@
.. _getting_started:
Getting Started: Building and Running lld
=========================================
This page gives you the shortest path to checking out and building lld. If you
run into problems, please file bugs in the `LLVM Bugzilla`__
__ http://llvm.org/bugs/
Building lld
------------
On Unix-like Systems
~~~~~~~~~~~~~~~~~~~~
1. Get the required tools.
* `CMake 2.8`_\+.
* make (or any build system CMake supports).
* `Clang 3.1`_\+ or GCC 4.7+ (C++11 support is required).
* If using Clang, you will also need `libc++`_.
* `Python 2.4`_\+ (not 3.x) for running tests.
.. _CMake 2.8: http://www.cmake.org/cmake/resources/software.html
.. _Clang 3.1: http://clang.llvm.org/
.. _libc++: http://libcxx.llvm.org/
.. _Python 2.4: http://python.org/download/
2. Check out LLVM::
$ cd path/to/llvm-project
$ svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm
3. Check out lld::
$ cd llvm/tools
$ svn co http://llvm.org/svn/llvm-project/lld/trunk lld
* lld can also be checked out to ``path/to/llvm-project`` and built as an external
project.
4. Build LLVM and lld::
$ cd path/to/llvm-build/llvm (out of source build required)
$ cmake -G "Unix Makefiles" path/to/llvm-project/llvm
$ make
* If you want to build with clang and it is not the default compiler or
it is installed in an alternate location, you'll need to tell the cmake tool
the location of the C and C++ compiler via CMAKE_C_COMPILER and
CMAKE_CXX_COMPILER. For example::
$ cmake -DCMAKE_CXX_COMPILER=/path/to/clang++ -DCMAKE_C_COMPILER=/path/to/clang ...
5. Test::
$ make lld-test
Using Visual Studio
~~~~~~~~~~~~~~~~~~~
#. Get the required tools.
* `CMake 2.8`_\+.
* `Visual Studio 11 (2012) or later`_ (required for C++11 support)
* `Python 2.4`_\+ (not 3.x) for running tests.
.. _CMake 2.8: http://www.cmake.org/cmake/resources/software.html
.. _Visual Studio 11 (2012) or later: http://www.microsoft.com/visualstudio/11/en-us
.. _Python 2.4: http://python.org/download/
#. Check out LLVM::
$ cd path/to/llvm-project
$ svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm
#. Check out lld::
$ cd llvm/tools
$ svn co http://llvm.org/svn/llvm-project/lld/trunk lld
* lld can also be checked out to ``path/to/llvm-project`` and built as an external
project.
#. Generate Visual Studio project files::
$ cd path/to/llvm-build/llvm (out of source build required)
$ cmake -G "Visual Studio 11" path/to/llvm-project/llvm
#. Build
* Open LLVM.sln in Visual Studio.
* Build the ``ALL_BUILD`` target.
#. Test
* Build the ``lld-test`` target.
More Information
~~~~~~~~~~~~~~~~
For more information on using CMake see the `LLVM CMake guide`_.
.. _LLVM CMake guide: http://llvm.org/docs/CMake.html

BIN
docs/hello.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

88
docs/index.rst Normal file
View file

@ -0,0 +1,88 @@
.. _index:
lld - The LLVM Linker
=====================
lld is a new set of modular code for creating linker tools.
* End-User Features:
* Compatible with existing linker options
* Reads standard Object Files (e.g. ELF, Mach-O, PE/COFF)
* Writes standard Executable Files (e.g. ELF, Mach-O, PE)
* Fast link times
* Minimal memory use
* Remove clang's reliance on "the system linker"
* Uses the LLVM `"UIUC" BSD-Style license`__.
* Applications:
* Modular design
* Support cross linking
* Easy to add new CPU support
* Can be built as static tool or library
* Design and Implementation:
* Extensive unit tests
* Internal linker model can be dumped/read to textual format
* Internal linker model can be dumped/read to a new native format
* Native format designed to be fast to read and write
* Additional linking features can be plugged in as "passes"
* OS specific and CPU specific code factored out
Why a new linker?
-----------------
The fact that clang relies on whatever linker tool you happen to have installed
means that clang has been very conservative adopting features which require a
recent linker.
In the same way that the MC layer of LLVM has removed clang's reliance on the
system assembler tool, the lld project will remove clang's reliance on the
system linker tool.
Current Status
--------------
lld can self host on x86-64 FreeBSD and Linux and x86 Windows.
All SingleSource tests in test-suite pass on x86-64 Linux.
All SingleSource and MultiSource tests in the LLVM test-suite
pass on MIPS 32-bit little-endian Linux.
Source
------
lld is available in the LLVM SVN repository::
svn co http://llvm.org/svn/llvm-project/lld/trunk lld
lld is also available via the read-only git mirror::
git clone http://llvm.org/git/lld.git
Put it in llvm's tools/ directory, rerun cmake, then build target lld.
Contents
--------
.. toctree::
:maxdepth: 2
design
getting_started
development
windows_support
open_projects
sphinx_intro
Indices and tables
------------------
* :ref:`genindex`
* :ref:`search`
__ http://llvm.org/docs/DeveloperPolicy.html#license

View file

@ -0,0 +1,22 @@
{#
sphinxdoc/layout.html
~~~~~~~~~~~~~~~~~~~~~
Sphinx layout template for the sphinxdoc theme.
:copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{% extends "basic/layout.html" %}
{% block relbar1 %}
<div class="logo">
<a href="{{ pathto('index') }}"><img src="{{
pathto("_static/logo.png", 1) }}" alt="LLVM Documentation"/></a>
</div>
{{ super() }}
{% endblock %}
{# put the sidebar before the body #}
{% block sidebar1 %}{{ sidebar() }}{% endblock %}
{% block sidebar2 %}{% endblock %}

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

View file

@ -0,0 +1,345 @@
/*
* sphinxdoc.css_t
* ~~~~~~~~~~~~~~~
*
* Sphinx stylesheet -- sphinxdoc theme. Originally created by
* Armin Ronacher for Werkzeug.
*
* :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
'Verdana', sans-serif;
font-size: 14px;
letter-spacing: -0.01em;
line-height: 150%;
text-align: center;
background-color: #BFD1D4;
color: black;
padding: 0;
border: 1px solid #aaa;
margin: 0px 80px 0px 80px;
min-width: 740px;
}
div.logo {
background-color: white;
text-align: left;
padding: 10px 10px 15px 15px;
}
div.document {
background-color: white;
text-align: left;
background-image: url(contents.png);
background-repeat: repeat-x;
}
div.bodywrapper {
margin: 0 240px 0 0;
border-right: 1px solid #ccc;
}
div.body {
margin: 0;
padding: 0.5em 20px 20px 20px;
}
div.related {
font-size: 1em;
}
div.related ul {
background-image: url(navigation.png);
height: 2em;
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
}
div.related ul li {
margin: 0;
padding: 0;
height: 2em;
float: left;
}
div.related ul li.right {
float: right;
margin-right: 5px;
}
div.related ul li a {
margin: 0;
padding: 0 5px 0 5px;
line-height: 1.75em;
color: #EE9816;
}
div.related ul li a:hover {
color: #3CA8E7;
}
div.sphinxsidebarwrapper {
padding: 0;
}
div.sphinxsidebar {
margin: 0;
padding: 0.5em 15px 15px 0;
width: 210px;
float: right;
font-size: 1em;
text-align: left;
}
div.sphinxsidebar h3, div.sphinxsidebar h4 {
margin: 1em 0 0.5em 0;
font-size: 1em;
padding: 0.1em 0 0.1em 0.5em;
color: white;
border: 1px solid #86989B;
background-color: #AFC1C4;
}
div.sphinxsidebar h3 a {
color: white;
}
div.sphinxsidebar ul {
padding-left: 1.5em;
margin-top: 7px;
padding: 0;
line-height: 130%;
}
div.sphinxsidebar ul ul {
margin-left: 20px;
}
div.footer {
background-color: #E3EFF1;
color: #86989B;
padding: 3px 8px 3px 0;
clear: both;
font-size: 0.8em;
text-align: right;
}
div.footer a {
color: #86989B;
text-decoration: underline;
}
/* -- body styles ----------------------------------------------------------- */
p {
margin: 0.8em 0 0.5em 0;
}
a {
color: #CA7900;
text-decoration: none;
}
a:hover {
color: #2491CF;
}
div.body a {
text-decoration: underline;
}
h1 {
margin: 0;
padding: 0.7em 0 0.3em 0;
font-size: 1.5em;
color: #11557C;
}
h2 {
margin: 1.3em 0 0.2em 0;
font-size: 1.35em;
padding: 0;
}
h3 {
margin: 1em 0 -0.3em 0;
font-size: 1.2em;
}
div.body h1 a, div.body h2 a, div.body h3 a, div.body h4 a, div.body h5 a, div.body h6 a {
color: black!important;
}
h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor {
display: none;
margin: 0 0 0 0.3em;
padding: 0 0.2em 0 0.2em;
color: #aaa!important;
}
h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor,
h5:hover a.anchor, h6:hover a.anchor {
display: inline;
}
h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover,
h5 a.anchor:hover, h6 a.anchor:hover {
color: #777;
background-color: #eee;
}
a.headerlink {
color: #c60f0f!important;
font-size: 1em;
margin-left: 6px;
padding: 0 4px 0 4px;
text-decoration: none!important;
}
a.headerlink:hover {
background-color: #ccc;
color: white!important;
}
cite, code, tt {
font-family: 'Consolas', 'Deja Vu Sans Mono',
'Bitstream Vera Sans Mono', monospace;
font-size: 0.95em;
letter-spacing: 0.01em;
}
tt {
background-color: #f2f2f2;
border-bottom: 1px solid #ddd;
color: #333;
}
tt.descname, tt.descclassname, tt.xref {
border: 0;
}
hr {
border: 1px solid #abc;
margin: 2em;
}
a tt {
border: 0;
color: #CA7900;
}
a tt:hover {
color: #2491CF;
}
pre {
font-family: 'Consolas', 'Deja Vu Sans Mono',
'Bitstream Vera Sans Mono', monospace;
font-size: 0.95em;
letter-spacing: 0.015em;
line-height: 120%;
padding: 0.5em;
border: 1px solid #ccc;
background-color: #f8f8f8;
}
pre a {
color: inherit;
text-decoration: underline;
}
td.linenos pre {
padding: 0.5em 0;
}
div.quotebar {
background-color: #f8f8f8;
max-width: 250px;
float: right;
padding: 2px 7px;
border: 1px solid #ccc;
}
div.topic {
background-color: #f8f8f8;
}
table {
border-collapse: collapse;
margin: 0 -0.5em 0 -0.5em;
}
table td, table th {
padding: 0.2em 0.5em 0.2em 0.5em;
}
div.admonition, div.warning {
font-size: 0.9em;
margin: 1em 0 1em 0;
border: 1px solid #86989B;
background-color: #f7f7f7;
padding: 0;
}
div.admonition p, div.warning p {
margin: 0.5em 1em 0.5em 1em;
padding: 0;
}
div.admonition pre, div.warning pre {
margin: 0.4em 1em 0.4em 1em;
}
div.admonition p.admonition-title,
div.warning p.admonition-title {
margin: 0;
padding: 0.1em 0 0.1em 0.5em;
color: white;
border-bottom: 1px solid #86989B;
font-weight: bold;
background-color: #AFC1C4;
}
div.warning {
border: 1px solid #940000;
}
div.warning p.admonition-title {
background-color: #CF0000;
border-bottom-color: #940000;
}
div.admonition ul, div.admonition ol,
div.warning ul, div.warning ol {
margin: 0.1em 0.5em 0.5em 3em;
padding: 0;
}
div.versioninfo {
margin: 1em 0 0 0;
border: 1px solid #ccc;
background-color: #DDEAF0;
padding: 8px;
line-height: 1.3em;
font-size: 0.9em;
}
.viewcode-back {
font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
'Verdana', sans-serif;
}
div.viewcode-block:target {
background-color: #f4debf;
border-top: 1px solid #ac9;
border-bottom: 1px solid #ac9;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

View file

@ -0,0 +1,4 @@
[theme]
inherit = basic
stylesheet = llvm.css
pygments_style = friendly

190
docs/make.bat Normal file
View file

@ -0,0 +1,190 @@
@ECHO OFF
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
set I18NSPHINXOPTS=%SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^<target^>` where ^<target^> is one of
echo. html to make standalone HTML files
echo. dirhtml to make HTML files named index.html in directories
echo. singlehtml to make a single large HTML file
echo. pickle to make pickle files
echo. json to make JSON files
echo. htmlhelp to make HTML files and a HTML help project
echo. qthelp to make HTML files and a qthelp project
echo. devhelp to make HTML files and a Devhelp project
echo. epub to make an epub
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo. text to make text files
echo. man to make manual pages
echo. texinfo to make Texinfo files
echo. gettext to make PO message catalogs
echo. changes to make an overview over all changed/added/deprecated items
echo. linkcheck to check all external links for integrity
echo. doctest to run all doctests embedded in the documentation if enabled
goto end
)
if "%1" == "clean" (
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
del /q /s %BUILDDIR%\*
goto end
)
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "singlehtml" (
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\lld.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\lld.ghc
goto end
)
if "%1" == "devhelp" (
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished.
goto end
)
if "%1" == "epub" (
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The epub file is in %BUILDDIR%/epub.
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
if errorlevel 1 exit /b 1
echo.
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "text" (
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The text files are in %BUILDDIR%/text.
goto end
)
if "%1" == "man" (
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The manual pages are in %BUILDDIR%/man.
goto end
)
if "%1" == "texinfo" (
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
goto end
)
if "%1" == "gettext" (
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
if errorlevel 1 exit /b 1
echo.
echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
if errorlevel 1 exit /b 1
echo.
echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
if errorlevel 1 exit /b 1
echo.
echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
:end

17
docs/open_projects.rst Normal file
View file

@ -0,0 +1,17 @@
.. _open_projects:
Open Projects
=============
.. include:: ../include/lld/Core/TODO.txt
.. include:: ../lib/Core/TODO.txt
.. include:: ../lib/Driver/TODO.rst
.. include:: ../lib/ReaderWriter/ELF/X86_64/TODO.rst
.. include:: ../lib/ReaderWriter/ELF/AArch64/TODO.rst
.. include:: ../lib/ReaderWriter/ELF/ARM/TODO.rst
.. include:: ../tools/lld/TODO.txt
Documentation TODOs
~~~~~~~~~~~~~~~~~~~
.. todolist::

147
docs/sphinx_intro.rst Normal file
View file

@ -0,0 +1,147 @@
.. _sphinx_intro:
Sphinx Introduction for LLVM Developers
=======================================
This document is intended as a short and simple introduction to the Sphinx
documentation generation system for LLVM developers.
Quickstart
----------
To get started writing documentation, you will need to:
1. Have the Sphinx tools :ref:`installed <installing_sphinx>`.
2. Understand how to :ref:`build the documentation
<building_the_documentation>`.
3. Start :ref:`writing documentation <writing_documentation>`!
.. _installing_sphinx:
Installing Sphinx
~~~~~~~~~~~~~~~~~
You should be able to install Sphinx using the standard Python package
installation tool ``easy_install``, as follows::
$ sudo easy_install sphinx
Searching for sphinx
Reading http://pypi.python.org/simple/sphinx/
Reading http://sphinx.pocoo.org/
Best match: Sphinx 1.1.3
... more lines here ..
If you do not have root access (or otherwise want to avoid installing Sphinx in
system directories) see the section on :ref:`installing_sphinx_in_a_venv` .
If you do not have the ``easy_install`` tool on your system, you should be able
to install it using:
Linux
Use your distribution's standard package management tool to install it,
i.e., ``apt-get install easy_install`` or ``yum install easy_install``.
Mac OS X
All modern Mac OS X systems come with ``easy_install`` as part of the base
system.
Windows
See the `setuptools <http://pypi.python.org/pypi/setuptools>`_ package web
page for instructions.
.. _building_the_documentation:
Building the documentation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In order to build the documentation, all you should need to do is change to the
``docs`` directory and invoke make as follows::
$ cd path/to/project/docs
$ make html
Note that on Windows there is a ``make.bat`` command in the docs directory which
supplies the same interface as the ``Makefile``.
That command will invoke ``sphinx-build`` with the appropriate options for the
project, and generate the HTML documentation in a ``_build`` subdirectory. You
can browse it starting from the index page by visiting
``_build/html/index.html``.
Sphinx supports a wide variety of generation formats (including LaTeX, man
pages, and plain text). The ``Makefile`` includes a number of convenience
targets for invoking ``sphinx-build`` appropriately, the common ones are:
make html
Generate the HTML output.
make latexpdf
Generate LaTeX documentation and convert to a PDF.
make man
Generate man pages.
.. _writing_documentation:
Writing documentation
~~~~~~~~~~~~~~~~~~~~~
The documentation itself is written in the reStructuredText (ReST) format, and Sphinx
defines additional tags to support features like cross-referencing.
The ReST format itself is organized around documents mostly being readable
plaintext documents. You should generally be able to write new documentation
easily just by following the style of the existing documentation.
If you want to understand the formatting of the documents more, the best place
to start is Sphinx's own `ReST Primer <http://sphinx.pocoo.org/rest.html>`_.
Learning More
-------------
If you want to learn more about the Sphinx system, the best place to start is
the Sphinx documentation itself, available `here
<http://sphinx.pocoo.org/contents.html>`_.
.. _installing_sphinx_in_a_venv:
Installing Sphinx in a Virtual Environment
------------------------------------------
Most Python developers prefer to work with tools inside a *virtualenv* (virtual
environment) instance, which functions as an application sandbox. This avoids
polluting your system installation with different packages used by various
projects (and ensures that dependencies for different packages don't conflict
with one another). Of course, you need to first have the virtualenv software
itself which generally would be installed at the system level::
$ sudo easy_install virtualenv
but after that you no longer need to install additional packages in the system
directories.
Once you have the *virtualenv* tool itself installed, you can create a
virtualenv for Sphinx using::
$ virtualenv ~/my-sphinx-install
New python executable in /Users/dummy/my-sphinx-install/bin/python
Installing setuptools............done.
Installing pip...............done.
$ ~/my-sphinx-install/bin/easy_install sphinx
... install messages here ...
and from now on you can "activate" the *virtualenv* using::
$ source ~/my-sphinx-install/bin/activate
which will change your PATH to ensure the sphinx-build tool from inside the
virtual environment will be used. See the `virtualenv website
<http://www.virtualenv.org/en/latest/index.html>`_ for more information on using
virtual environments.

118
docs/windows_support.rst Normal file
View file

@ -0,0 +1,118 @@
.. raw:: html
<style type="text/css">
.none { background-color: #FFCCCC }
.partial { background-color: #FFFF99 }
.good { background-color: #CCFF99 }
</style>
.. role:: none
.. role:: partial
.. role:: good
===============
Windows support
===============
LLD has some experimental Windows support. When invoked as ``link.exe`` or with
``-flavor link``, the driver for Windows operating system is used to parse
command line options, and it drives further linking processes. LLD accepts
almost all command line options that the linker shipped with Microsoft Visual
C++ (link.exe) supports.
The current status is that LLD can link itself on Windows x86 using Visual C++
2012 or 2013 as the compiler.
Development status
==================
Driver
:good:`Mostly done`. Some exotic command line options that are not usually
used for application develompent, such as ``/DRIVER``, are not supported.
Options for Windows 8 app store are not recognized too
(e.g. ``/APPCONTAINER``).
Linking against DLL
:good:`Done`. LLD can read import libraries needed to link against DLL. Both
export-by-name and export-by-ordinal are supported.
Linking against static library
:good:`Done`. The format of static library (.lib) on Windows is actually the
same as on Unix (.a). LLD can read it.
Creating DLL
:good:`Done`. LLD creates a DLL if ``/DLL`` option is given. Exported
functions can be specified either via command line (``/EXPORT``) or via
module-definition file (.def). Both export-by-name and export-by-ordinal are
supported. LLD uses Microsoft ``lib.exe`` tool to create an import library
file.
Windows resource files support
:good:`Done`. If an ``.rc`` file is given, LLD converts the file to a COFF
file using some external commands and link it. Specifically, ``rc.exe`` is
used to compile a resource file (.rc) to a compiled resource (.res)
file. ``rescvt.exe`` is then used to convert a compiled resource file to a
COFF object file section. Both tools are shipped with MSVC.
Safe Structured Exception Handler (SEH)
:good:`Done` for x86. :partial:`Work in progress` for x64.
Module-definition file
:partial:`Partially done`. LLD currently recognizes these directives:
``EXPORTS``, ``HEAPSIZE``, ``STACKSIZE``, ``NAME``, and ``VERSION``.
x64 (x86-64)
:partial:`Work in progress`. LLD can create PE32+ executable but the generated
file does not work unless source object files are very simple because of the
lack of SEH handler table.
Debug info
:none:`No progress has been made`. Microsoft linker can interpret the CodeGen
debug info (old-style debug info) and PDB to emit an .pdb file. LLD doesn't
support neither.
Building LLD
============
Using Visual Studio IDE/MSBuild
-------------------------------
1. Check out LLVM and LLD from the LLVM SVN repository (or Git mirror),
#. run ``cmake -G "Visual Studio 12" <llvm-source-dir>`` from VS command prompt,
#. open LLVM.sln with Visual Studio, and
#. build ``lld`` target in ``lld executables`` folder
Alternatively, you can use msbuild if you don't like to work in an IDE::
msbuild LLVM.sln /m /target:"lld executables\lld"
MSBuild.exe had been shipped as a component of the .NET framework, but since
2013 it's part of Visual Studio. You can find it at "C:\\Program Files
(x86)\\msbuild".
You can build LLD as a 64 bit application. To do that, open VS2013 x64 command
prompt and run cmake for "Visual Studio 12 Win64" target.
Using Ninja
-----------
1. Check out LLVM and LLD from the LLVM SVN repository (or Git mirror),
#. run ``cmake -G ninja <llvm-source-dir>`` from VS command prompt,
#. run ``ninja lld``
Known issues
============
Note that LLD is still in early stage in development, so there are still many
bugs. Here is a list of notable bugs.
* Symbol name resolution from library files sometimes fails. On Windows, the
order of library files in command line does not matter, but LLD sometimes
fails to simulate the semantics. A workaround for it is to explicitly add
library files to command line with ``/DEFAULTLIB``.
* Subsystem inference is not very reliable. Linker is supposed to set
``subsystem`` field in the PE/COFF header according to entry function name,
but LLD sometimes ended up with ``unknown`` subsystem type. You need to give
``/SUBSYSTEM`` option if it fails to infer it.

4
include/Makefile Normal file
View file

@ -0,0 +1,4 @@
LLD_LEVEL := ..
DIRS := lld
include $(LLD_LEVEL)/Makefile

View file

@ -0,0 +1,32 @@
LLD_LEVEL := ../../..
BUILT_SOURCES = Version.inc
TABLEGEN_INC_FILES_COMMON = 1
include $(LLD_LEVEL)/Makefile
# Compute the lld version from the LLVM version, unless specified explicitly.
ifndef LLD_VERSION
LLD_VERSION := $(subst svn,,$(LLVMVersion))
LLD_VERSION := $(subst rc,,$(LLD_VERSION))
endif
LLD_VERSION_COMPONENTS := $(subst ., ,$(LLD_VERSION))
LLD_VERSION_MAJOR := $(word 1,$(LLD_VERSION_COMPONENTS))
LLD_VERSION_MINOR := $(word 2,$(LLD_VERSION_COMPONENTS))
LLD_REVISION := $(strip \
$(shell $(LLVM_SRC_ROOT)/utils/GetSourceVersion $(LLVM_SRC_ROOT)/tools/lld))
LLD_REPOSITORY := $(strip \
$(shell $(LLVM_SRC_ROOT)/utils/GetRepositoryPath $(LLVM_SRC_ROOT)/tools/lld))
$(ObjDir)/Version.inc.tmp : Version.inc.in Makefile $(LLVM_OBJ_ROOT)/Makefile.config $(ObjDir)/.dir
$(Echo) "Updating LLD version info."
$(Verb)sed -e "s#@LLD_VERSION@#$(LLD_VERSION)#g" \
-e "s#@LLD_VERSION_MAJOR@#$(LLD_VERSION_MAJOR)#g" \
-e "s#@LLD_VERSION_MINOR@#$(LLD_VERSION_MINOR)#g" \
-e "s#@LLD_REVISION@#$(LLD_REVISION)#g" \
-e "s#@LLD_REPOSITORY@#$(LLD_REPOSITORY)#g" \
$< > $@

View file

@ -0,0 +1,51 @@
//===- lld/Config/Version.h - LLD Version Number ----------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Defines version macros and version-related utility functions
/// for lld.
///
//===----------------------------------------------------------------------===//
#ifndef LLD_VERSION_H
#define LLD_VERSION_H
#include "lld/Config/Version.inc"
#include "llvm/ADT/StringRef.h"
#include <string>
/// \brief Helper macro for LLD_VERSION_STRING.
#define LLD_MAKE_VERSION_STRING2(X) #X
/// \brief Helper macro for LLD_VERSION_STRING.
#define LLD_MAKE_VERSION_STRING(X, Y) LLD_MAKE_VERSION_STRING2(X.Y)
/// \brief A string that describes the lld version number, e.g., "1.0".
#define LLD_VERSION_STRING \
LLD_MAKE_VERSION_STRING(LLD_VERSION_MAJOR, LLD_VERSION_MINOR)
namespace lld {
/// \brief Retrieves the repository path (e.g., Subversion path) that
/// identifies the particular lld branch, tag, or trunk from which this
/// lld was built.
llvm::StringRef getLLDRepositoryPath();
/// \brief Retrieves the repository revision number (or identifer) from which
/// this lld was built.
llvm::StringRef getLLDRevision();
/// \brief Retrieves the full repository version that is an amalgamation of
/// the information in getLLDRepositoryPath() and getLLDRevision().
std::string getLLDRepositoryVersion();
/// \brief Retrieves a string representing the complete lld version.
llvm::StringRef getLLDVersion();
}
#endif // LLD_VERSION_H

View file

@ -0,0 +1,5 @@
#define LLD_VERSION @LLD_VERSION@
#define LLD_VERSION_MAJOR @LLD_VERSION_MAJOR@
#define LLD_VERSION_MINOR @LLD_VERSION_MINOR@
#define LLD_REVISION_STRING "@LLD_REVISION@"
#define LLD_REPOSITORY_STRING "@LLD_REPOSITORY@"

View file

@ -0,0 +1,43 @@
//===- Core/AbsoluteAtom.h - An absolute Atom -----------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_ABSOLUTE_ATOM_H
#define LLD_CORE_ABSOLUTE_ATOM_H
#include "lld/Core/Atom.h"
namespace lld {
/// An AbsoluteAtom has no content.
/// It exists to represent content at fixed addresses in memory.
class AbsoluteAtom : public Atom {
public:
virtual uint64_t value() const = 0;
/// scope - The visibility of this atom to other atoms. C static functions
/// have scope scopeTranslationUnit. Regular C functions have scope
/// scopeGlobal. Functions compiled with visibility=hidden have scope
/// scopeLinkageUnit so they can be see by other atoms being linked but not
/// by the OS loader.
virtual Scope scope() const = 0;
static bool classof(const Atom *a) {
return a->definition() == definitionAbsolute;
}
static bool classof(const AbsoluteAtom *) { return true; }
protected:
AbsoluteAtom() : Atom(definitionAbsolute) {}
};
} // namespace lld
#endif // LLD_CORE_ABSOLUTE_ATOM_H

102
include/lld/Core/Alias.h Normal file
View file

@ -0,0 +1,102 @@
//===- lld/Core/Alias.h - Alias atoms -------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Provide alias atoms.
///
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_ALIAS_H
#define LLD_CORE_ALIAS_H
#include "lld/Core/LLVM.h"
#include "lld/Core/Simple.h"
#include "llvm/ADT/Optional.h"
#include <string>
namespace lld {
// An AliasAtom is a zero-size atom representing an alias for other atom. It has
// a LayoutAfter reference to the target atom, so that this atom and the target
// atom will be laid out at the same location in the final result. Initially
// the target atom is an undefined atom. Resolver will replace it with a defined
// one.
//
// It does not have attributes itself. Most member function calls are forwarded
// to the target atom.
class AliasAtom : public SimpleDefinedAtom {
public:
AliasAtom(const File &file, StringRef name)
: SimpleDefinedAtom(file), _target(nullptr), _name(name),
_merge(DefinedAtom::mergeNo), _deadStrip(DefinedAtom::deadStripNormal) {
}
StringRef name() const override { return _name; }
uint64_t size() const override { return 0; }
ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); }
Scope scope() const override {
getTarget();
return _target ? _target->scope() : scopeLinkageUnit;
}
Merge merge() const override {
if (_merge.hasValue())
return _merge.getValue();
getTarget();
return _target ? _target->merge() : mergeNo;
}
void setMerge(Merge val) { _merge = val; }
ContentType contentType() const override {
getTarget();
return _target ? _target->contentType() : typeUnknown;
}
Interposable interposable() const override {
getTarget();
return _target ? _target->interposable() : interposeNo;
}
SectionChoice sectionChoice() const override {
getTarget();
return _target ? _target->sectionChoice() : sectionBasedOnContent;
}
StringRef customSectionName() const override {
getTarget();
return _target ? _target->customSectionName() : StringRef("");
}
DeadStripKind deadStrip() const override { return _deadStrip; }
void setDeadStrip(DeadStripKind val) { _deadStrip = val; }
private:
void getTarget() const {
if (_target)
return;
for (const Reference *r : *this) {
if (r->kindNamespace() == lld::Reference::KindNamespace::all &&
r->kindValue() == lld::Reference::kindLayoutAfter) {
_target = dyn_cast<DefinedAtom>(r->target());
return;
}
}
}
mutable const DefinedAtom *_target;
std::string _name;
llvm::Optional<Merge> _merge;
DeadStripKind _deadStrip;
};
} // end namespace lld
#endif

View file

@ -0,0 +1,60 @@
//===- Core/ArchiveLibraryFile.h - Models static library ------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_ARCHIVE_LIBRARY_FILE_H
#define LLD_CORE_ARCHIVE_LIBRARY_FILE_H
#include "lld/Core/File.h"
#include "lld/Core/Parallel.h"
#include <set>
namespace lld {
///
/// The ArchiveLibraryFile subclass of File is used to represent unix
/// static library archives. These libraries provide no atoms to the
/// initial set of atoms linked. Instead, when the Resolver will query
/// ArchiveLibraryFile instances for specific symbols names using the
/// find() method. If the archive contains an object file which has a
/// DefinedAtom whose scope is not translationUnit, then that entire
/// object file File is returned.
///
class ArchiveLibraryFile : public File {
public:
static bool classof(const File *f) {
return f->kind() == kindArchiveLibrary;
}
/// Check if any member of the archive contains an Atom with the
/// specified name and return the File object for that member, or nullptr.
virtual File *find(StringRef name, bool dataSymbolOnly) = 0;
virtual std::error_code
parseAllMembers(std::vector<std::unique_ptr<File>> &result) = 0;
// Parses a member file containing a given symbol, so that when you
// need the file find() can return that immediately. Calling this function
// has no side effect other than pre-instantiating a file. Calling this
// function doesn't affect correctness.
virtual void preload(TaskGroup &group, StringRef symbolName) {}
/// Returns a set of all defined symbols in the archive, i.e. all
/// resolvable symbol using this file.
virtual std::set<StringRef> getDefinedSymbols() {
return std::set<StringRef>();
}
protected:
/// only subclasses of ArchiveLibraryFile can be instantiated
ArchiveLibraryFile(StringRef path) : File(path, kindArchiveLibrary) {}
};
} // namespace lld
#endif // LLD_CORE_ARCHIVE_LIBRARY_FILE_H

76
include/lld/Core/Atom.h Normal file
View file

@ -0,0 +1,76 @@
//===- Core/Atom.h - A node in linking graph ------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_ATOM_H
#define LLD_CORE_ATOM_H
#include "lld/Core/LLVM.h"
namespace lld {
class File;
///
/// The linker has a Graph Theory model of linking. An object file is seen
/// as a set of Atoms with References to other Atoms. Each Atom is a node
/// and each Reference is an edge. An Atom can be a DefinedAtom which has
/// content or a UndefinedAtom which is a placeholder and represents an
/// undefined symbol (extern declaration).
///
class Atom {
public:
/// Whether this atom is defined or a proxy for an undefined symbol
enum Definition {
definitionRegular, ///< Normal C/C++ function or global variable.
definitionAbsolute, ///< Asm-only (foo = 10). Not tied to any content.
definitionUndefined, ///< Only in .o files to model reference to undef.
definitionSharedLibrary ///< Only in shared libraries to model export.
};
/// The scope in which this atom is acessible to other atoms.
enum Scope {
scopeTranslationUnit, ///< Accessible only to atoms in the same translation
/// unit (e.g. a C static).
scopeLinkageUnit, ///< Accessible to atoms being linked but not visible
/// to runtime loader (e.g. visibility=hidden).
scopeGlobal ///< Accessible to all atoms and visible to runtime
/// loader (e.g. visibility=default).
};
/// file - returns the File that produced/owns this Atom
virtual const File& file() const = 0;
/// name - The name of the atom. For a function atom, it is the (mangled)
/// name of the function.
virtual StringRef name() const = 0;
/// definition - Whether this atom is a definition or represents an undefined
/// symbol.
Definition definition() const { return _definition; }
static bool classof(const Atom *a) { return true; }
protected:
/// Atom is an abstract base class. Only subclasses can access constructor.
explicit Atom(Definition def) : _definition(def) {}
/// The memory for Atom objects is always managed by the owning File
/// object. Therefore, no one but the owning File object should call
/// delete on an Atom. In fact, some File objects may bulk allocate
/// an array of Atoms, so they cannot be individually deleted by anyone.
virtual ~Atom() {}
private:
Definition _definition;
};
} // namespace lld
#endif // LLD_CORE_ATOM_H

View file

@ -0,0 +1,368 @@
//===- Core/DefinedAtom.h - An Atom with content --------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_DEFINED_ATOM_H
#define LLD_CORE_DEFINED_ATOM_H
#include "lld/Core/Atom.h"
#include "lld/Core/LLVM.h"
namespace lld {
class File;
class Reference;
/// \brief The fundamental unit of linking.
///
/// A C function or global variable is an atom. An atom has content and
/// attributes. The content of a function atom is the instructions that
/// implement the function. The content of a global variable atom is its
/// initial bytes.
///
/// Here are some example attribute sets for common atoms. If a particular
/// attribute is not listed, the default values are: definition=regular,
/// sectionChoice=basedOnContent, scope=translationUnit, merge=no,
/// deadStrip=normal, interposable=no
///
/// C function: void foo() {} <br>
/// name=foo, type=code, perm=r_x, scope=global
///
/// C static function: staic void func() {} <br>
/// name=func, type=code, perm=r_x
///
/// C global variable: int count = 1; <br>
/// name=count, type=data, perm=rw_, scope=global
///
/// C tentative definition: int bar; <br>
/// name=bar, type=zerofill, perm=rw_, scope=global,
/// merge=asTentative, interposable=yesAndRuntimeWeak
///
/// Uninitialized C static variable: static int stuff; <br>
/// name=stuff, type=zerofill, perm=rw_
///
/// Weak C function: __attribute__((weak)) void foo() {} <br>
/// name=foo, type=code, perm=r_x, scope=global, merge=asWeak
///
/// Hidden C function: __attribute__((visibility("hidden"))) void foo() {}<br>
/// name=foo, type=code, perm=r_x, scope=linkageUnit
///
/// No-dead-strip function: __attribute__((used)) void foo() {} <br>
/// name=foo, type=code, perm=r_x, scope=global, deadStrip=never
///
/// Non-inlined C++ inline method: inline void Foo::doit() {} <br>
/// name=_ZN3Foo4doitEv, type=code, perm=r_x, scope=global,
/// mergeDupes=asWeak
///
/// Non-inlined C++ inline method whose address is taken:
/// inline void Foo::doit() {} <br>
/// name=_ZN3Foo4doitEv, type=code, perm=r_x, scope=global,
/// mergeDupes=asAddressedWeak
///
/// literal c-string: "hello" <br>
/// name="" type=cstring, perm=r__, scope=linkageUnit
///
/// literal double: 1.234 <br>
/// name="" type=literal8, perm=r__, scope=linkageUnit
///
/// constant: { 1,2,3 } <br>
/// name="" type=constant, perm=r__, scope=linkageUnit
///
/// Pointer to initializer function: <br>
/// name="" type=initializer, perm=rw_l,
/// sectionChoice=customRequired
///
/// C function place in custom section: __attribute__((section("__foo")))
/// void foo() {} <br>
/// name=foo, type=code, perm=r_x, scope=global,
/// sectionChoice=customRequired, customSectionName=__foo
///
class DefinedAtom : public Atom {
public:
enum Interposable {
interposeNo, // linker can directly bind uses of this atom
interposeYes, // linker must indirect (through GOT) uses
interposeYesAndRuntimeWeak // must indirect and mark symbol weak in final
// linked image
};
enum Merge {
mergeNo, // Another atom with same name is error
mergeAsTentative, // Is ANSI C tentative definition, can be coalesced
mergeAsWeak, // Is C++ inline definition that was not inlined,
// but address was not taken, so atom can be hidden
// by linker
mergeAsWeakAndAddressUsed, // Is C++ definition inline definition whose
// address was taken.
mergeSameNameAndSize, // Another atom with different size is error
mergeByLargestSection, // Choose an atom whose section is the largest.
mergeByContent, // Merge with other constants with same content.
};
enum ContentType {
typeUnknown, // for use with definitionUndefined
typeCode, // executable code
typeResolver, // function which returns address of target
typeBranchIsland, // linker created for large binaries
typeBranchShim, // linker created to switch thumb mode
typeStub, // linker created for calling external function
typeStubHelper, // linker created for initial stub binding
typeConstant, // a read-only constant
typeCString, // a zero terminated UTF8 C string
typeUTF16String, // a zero terminated UTF16 string
typeCFI, // a FDE or CIE from dwarf unwind info
typeLSDA, // extra unwinding info
typeLiteral4, // a four-btye read-only constant
typeLiteral8, // an eight-btye read-only constant
typeLiteral16, // a sixteen-btye read-only constant
typeData, // read-write data
typeDataFast, // allow data to be quickly accessed
typeZeroFill, // zero-fill data
typeZeroFillFast, // allow zero-fill data to be quicky accessed
typeConstData, // read-only data after dynamic linker is done
typeObjC1Class, // ObjC1 class [Darwin]
typeLazyPointer, // pointer through which a stub jumps
typeLazyDylibPointer, // pointer through which a stub jumps [Darwin]
typeCFString, // NS/CFString object [Darwin]
typeGOT, // pointer to external symbol
typeInitializerPtr, // pointer to initializer function
typeTerminatorPtr, // pointer to terminator function
typeCStringPtr, // pointer to UTF8 C string [Darwin]
typeObjCClassPtr, // pointer to ObjC class [Darwin]
typeObjC2CategoryList, // pointers to ObjC category [Darwin]
typeDTraceDOF, // runtime data for Dtrace [Darwin]
typeInterposingTuples, // tuples of interposing info for dyld [Darwin]
typeTempLTO, // temporary atom for bitcode reader
typeCompactUnwindInfo, // runtime data for unwinder [Darwin]
typeProcessedUnwindInfo,// compressed compact unwind info [Darwin]
typeThunkTLV, // thunk used to access a TLV [Darwin]
typeTLVInitialData, // initial data for a TLV [Darwin]
typeTLVInitialZeroFill, // TLV initial zero fill data [Darwin]
typeTLVInitializerPtr, // pointer to thread local initializer [Darwin]
typeMachHeader, // atom representing mach_header [Darwin]
typeThreadZeroFill, // Uninitialized thread local data(TBSS) [ELF]
typeThreadData, // Initialized thread local data(TDATA) [ELF]
typeRONote, // Identifies readonly note sections [ELF]
typeRWNote, // Identifies readwrite note sections [ELF]
typeNoAlloc, // Identifies non allocatable sections [ELF]
typeGroupComdat, // Identifies a section group [ELF, COFF]
typeGnuLinkOnce, // Identifies a gnu.linkonce section [ELF]
};
// Permission bits for atoms and segments. The order of these values are
// important, because the layout pass may sort atoms by permission if other
// attributes are the same.
enum ContentPermissions {
perm___ = 0, // mapped as unaccessible
permR__ = 8, // mapped read-only
permRW_ = 8 + 2, // mapped readable and writable
permRW_L = 8 + 2 + 1, // initially mapped r/w, then made read-only
// loader writable
permR_X = 8 + 4, // mapped readable and executable
permRWX = 8 + 2 + 4, // mapped readable and writable and executable
permUnknown = 16 // unknown or invalid permissions
};
enum SectionChoice {
sectionBasedOnContent, // linker infers final section based on content
sectionCustomPreferred, // linker may place in specific section
sectionCustomRequired // linker must place in specific section
};
enum DeadStripKind {
deadStripNormal, // linker may dead strip this atom
deadStripNever, // linker must never dead strip this atom
deadStripAlways // linker must remove this atom if unused
};
enum DynamicExport {
/// \brief The linker may or may not export this atom dynamically depending
/// on the output type and other context of the link.
dynamicExportNormal,
/// \brief The linker will always export this atom dynamically.
dynamicExportAlways,
};
// Attributes describe a code model used by the atom.
enum CodeModel {
codeNA, // no specific code model
codeMipsPIC, // PIC function in a PIC / non-PIC mixed file
codeMipsMicro, // microMIPS instruction encoding
codeMipsMicroPIC, // microMIPS instruction encoding + PIC
codeMips16, // MIPS-16 instruction encoding
codeARMThumb, // ARM Thumb instruction set
};
struct Alignment {
Alignment(int p2, int m = 0)
: powerOf2(p2)
, modulus(m) {}
uint16_t powerOf2;
uint16_t modulus;
bool operator==(const Alignment &rhs) const {
return (powerOf2 == rhs.powerOf2) && (modulus == rhs.modulus);
}
};
/// \brief returns a value for the order of this Atom within its file.
///
/// This is used by the linker to order the layout of Atoms so that the
/// resulting image is stable and reproducible.
///
/// Note that this should not be confused with ordinals of exported symbols in
/// Windows DLLs. In Windows terminology, ordinals are symbols' export table
/// indices (small integers) which can be used instead of symbol names to
/// refer items in a DLL.
virtual uint64_t ordinal() const = 0;
/// \brief the number of bytes of space this atom's content will occupy in the
/// final linked image.
///
/// For a function atom, it is the number of bytes of code in the function.
virtual uint64_t size() const = 0;
/// \brief The size of the section from which the atom is instantiated.
///
/// Merge::mergeByLargestSection is defined in terms of section size
/// and not in terms of atom size, so we need this function separate
/// from size().
virtual uint64_t sectionSize() const { return 0; }
/// \brief The visibility of this atom to other atoms.
///
/// C static functions have scope scopeTranslationUnit. Regular C functions
/// have scope scopeGlobal. Functions compiled with visibility=hidden have
/// scope scopeLinkageUnit so they can be see by other atoms being linked but
/// not by the OS loader.
virtual Scope scope() const = 0;
/// \brief Whether the linker should use direct or indirect access to this
/// atom.
virtual Interposable interposable() const = 0;
/// \brief how the linker should handle if multiple atoms have the same name.
virtual Merge merge() const = 0;
/// \brief The type of this atom, such as code or data.
virtual ContentType contentType() const = 0;
/// \brief The alignment constraints on how this atom must be laid out in the
/// final linked image (e.g. 16-byte aligned).
virtual Alignment alignment() const = 0;
/// \brief Whether this atom must be in a specially named section in the final
/// linked image, or if the linker can infer the section based on the
/// contentType().
virtual SectionChoice sectionChoice() const = 0;
/// \brief If sectionChoice() != sectionBasedOnContent, then this return the
/// name of the section the atom should be placed into.
virtual StringRef customSectionName() const = 0;
/// \brief constraints on whether the linker may dead strip away this atom.
virtual DeadStripKind deadStrip() const = 0;
/// \brief Under which conditions should this atom be dynamically exported.
virtual DynamicExport dynamicExport() const {
return dynamicExportNormal;
}
/// \brief Code model used by the atom.
virtual CodeModel codeModel() const { return codeNA; }
/// \brief Returns the OS memory protections required for this atom's content
/// at runtime.
///
/// A function atom is R_X, a global variable is RW_, and a read-only constant
/// is R__.
virtual ContentPermissions permissions() const;
/// \brief returns a reference to the raw (unrelocated) bytes of this Atom's
/// content.
virtual ArrayRef<uint8_t> rawContent() const = 0;
/// This class abstracts iterating over the sequence of References
/// in an Atom. Concrete instances of DefinedAtom must implement
/// the derefIterator() and incrementIterator() methods.
class reference_iterator {
public:
reference_iterator(const DefinedAtom &a, const void *it)
: _atom(a), _it(it) { }
const Reference *operator*() const {
return _atom.derefIterator(_it);
}
const Reference *operator->() const {
return _atom.derefIterator(_it);
}
bool operator!=(const reference_iterator &other) const {
return _it != other._it;
}
reference_iterator &operator++() {
_atom.incrementIterator(_it);
return *this;
}
private:
const DefinedAtom &_atom;
const void *_it;
};
/// \brief Returns an iterator to the beginning of this Atom's References.
virtual reference_iterator begin() const = 0;
/// \brief Returns an iterator to the end of this Atom's References.
virtual reference_iterator end() const = 0;
static bool classof(const Atom *a) {
return a->definition() == definitionRegular;
}
/// Utility for deriving permissions from content type
static ContentPermissions permissions(ContentType type);
/// Utility function to check if the atom occupies file space
bool occupiesDiskSpace() const {
ContentType atomContentType = contentType();
return !(atomContentType == DefinedAtom::typeZeroFill ||
atomContentType == DefinedAtom::typeZeroFillFast ||
atomContentType == DefinedAtom::typeTLVInitialZeroFill ||
atomContentType == DefinedAtom::typeThreadZeroFill);
}
/// Utility function to check if the atom belongs to a group section
/// that represents section groups or .gnu.linkonce sections.
bool isGroupParent() const {
ContentType atomContentType = contentType();
return (atomContentType == DefinedAtom::typeGroupComdat ||
atomContentType == DefinedAtom::typeGnuLinkOnce);
}
// Returns true if lhs should be placed before rhs in the final output.
static bool compareByPosition(const DefinedAtom *lhs,
const DefinedAtom *rhs);
protected:
// DefinedAtom is an abstract base class. Only subclasses can access
// constructor.
DefinedAtom() : Atom(definitionRegular) { }
/// \brief Returns a pointer to the Reference object that the abstract
/// iterator "points" to.
virtual const Reference *derefIterator(const void *iter) const = 0;
/// \brief Adjusts the abstract iterator to "point" to the next Reference
/// object for this Atom.
virtual void incrementIterator(const void *&iter) const = 0;
};
} // end namespace lld
#endif

82
include/lld/Core/Error.h Normal file
View file

@ -0,0 +1,82 @@
//===- Error.h - system_error extensions for lld ----------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This declares a new error_category for the lld library.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_ERROR_H
#define LLD_CORE_ERROR_H
#include "lld/Core/LLVM.h"
#include <system_error>
namespace lld {
const std::error_category &native_reader_category();
enum class NativeReaderError {
success = 0,
unknown_file_format,
file_too_short,
file_malformed,
unknown_chunk_type,
memory_error,
conflicting_target_machine,
};
inline std::error_code make_error_code(NativeReaderError e) {
return std::error_code(static_cast<int>(e), native_reader_category());
}
const std::error_category &YamlReaderCategory();
enum class YamlReaderError {
success = 0,
unknown_keyword,
illegal_value
};
inline std::error_code make_error_code(YamlReaderError e) {
return std::error_code(static_cast<int>(e), YamlReaderCategory());
}
const std::error_category &LinkerScriptReaderCategory();
enum class LinkerScriptReaderError {
success = 0,
parse_error,
unknown_symbol_in_expr,
unrecognized_function_in_expr
};
inline std::error_code make_error_code(LinkerScriptReaderError e) {
return std::error_code(static_cast<int>(e), LinkerScriptReaderCategory());
}
/// Creates an error_code object that has associated with it an arbitrary
/// error messsage. The value() of the error_code will always be non-zero
/// but its value is meaningless. The messsage() will be (a copy of) the
/// supplied error string.
/// Note: Once ErrorOr<> is updated to work with errors other than error_code,
/// this can be updated to return some other kind of error.
std::error_code make_dynamic_error_code(StringRef msg);
std::error_code make_dynamic_error_code(const Twine &msg);
} // end namespace lld
namespace std {
template <>
struct is_error_code_enum<lld::NativeReaderError> : std::true_type {};
template <> struct is_error_code_enum<lld::YamlReaderError> : std::true_type {};
template <>
struct is_error_code_enum<lld::LinkerScriptReaderError> : std::true_type {};
}
#endif

324
include/lld/Core/File.h Normal file
View file

@ -0,0 +1,324 @@
//===- Core/File.h - A Container of Atoms ---------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_FILE_H
#define LLD_CORE_FILE_H
#include "lld/Core/AbsoluteAtom.h"
#include "lld/Core/DefinedAtom.h"
#include "lld/Core/SharedLibraryAtom.h"
#include "lld/Core/UndefinedAtom.h"
#include "lld/Core/range.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/ErrorHandling.h"
#include <functional>
#include <memory>
#include <mutex>
#include <vector>
namespace lld {
class LinkingContext;
/// Every Atom is owned by some File. A common scenario is for a single
/// object file (.o) to be parsed by some reader and produce a single
/// File object that represents the content of that object file.
///
/// To iterate through the Atoms in a File there are four methods that
/// return collections. For instance to iterate through all the DefinedAtoms
/// in a File object use:
/// for (const DefinedAtoms *atom : file->defined()) {
/// }
///
/// The Atom objects in a File are owned by the File object. The Atom objects
/// are destroyed when the File object is destroyed.
class File {
public:
virtual ~File();
/// \brief Kinds of files that are supported.
enum Kind {
kindObject, ///< object file (.o)
kindSharedLibrary, ///< shared library (.so)
kindArchiveLibrary ///< archive (.a)
};
/// \brief Returns file kind. Need for dyn_cast<> on File objects.
Kind kind() const {
return _kind;
}
/// This returns the path to the file which was used to create this object
/// (e.g. "/tmp/foo.o"). If the file is a member of an archive file, the
/// returned string includes the archive file name.
StringRef path() const {
if (_archivePath.empty())
return _path;
if (_archiveMemberPath.empty())
_archiveMemberPath = (_archivePath + "(" + _path + ")").str();
return _archiveMemberPath;
}
/// Returns the path of the archive file name if this file is instantiated
/// from an archive file. Otherwise returns the empty string.
StringRef archivePath() const { return _archivePath; }
void setArchivePath(StringRef path) { _archivePath = path; }
/// Returns the path name of this file. It doesn't include archive file name.
StringRef memberPath() const { return _path; }
/// Returns the command line order of the file.
uint64_t ordinal() const {
assert(_ordinal != UINT64_MAX);
return _ordinal;
}
/// Returns true/false depending on whether an ordinal has been set.
bool hasOrdinal() const { return (_ordinal != UINT64_MAX); }
/// Sets the command line order of the file.
void setOrdinal(uint64_t ordinal) const { _ordinal = ordinal; }
template <typename T> class atom_iterator; // forward reference
/// For allocating any objects owned by this File.
llvm::BumpPtrAllocator &allocator() const {
return _allocator;
}
/// \brief For use interating over DefinedAtoms in this File.
typedef atom_iterator<DefinedAtom> defined_iterator;
/// \brief For use interating over UndefinedAtoms in this File.
typedef atom_iterator<UndefinedAtom> undefined_iterator;
/// \brief For use interating over SharedLibraryAtoms in this File.
typedef atom_iterator<SharedLibraryAtom> shared_library_iterator;
/// \brief For use interating over AbsoluteAtoms in this File.
typedef atom_iterator<AbsoluteAtom> absolute_iterator;
/// \brief Different object file readers may instantiate and manage atoms with
/// different data structures. This class is a collection abstraction.
/// Each concrete File instance must implement these atom_collection
/// methods to enable clients to interate the File's atoms.
template <typename T>
class atom_collection {
public:
virtual ~atom_collection() { }
virtual atom_iterator<T> begin() const = 0;
virtual atom_iterator<T> end() const = 0;
virtual const T *deref(const void *it) const = 0;
virtual void next(const void *&it) const = 0;
virtual uint64_t size() const = 0;
bool empty() const { return size() == 0; }
};
/// \brief The class is the iterator type used to iterate through a File's
/// Atoms. This iterator delegates the work to the associated atom_collection
/// object. There are four kinds of Atoms, so this iterator is templated on
/// the four base Atom kinds.
template <typename T>
class atom_iterator : public std::iterator<std::forward_iterator_tag, T> {
public:
atom_iterator(const atom_collection<T> &c, const void *it)
: _collection(&c), _it(it) { }
const T *operator*() const {
return _collection->deref(_it);
}
const T *operator->() const {
return _collection->deref(_it);
}
friend bool operator==(const atom_iterator<T> &lhs, const atom_iterator<T> &rhs) {
return lhs._it == rhs._it;
}
friend bool operator!=(const atom_iterator<T> &lhs, const atom_iterator<T> &rhs) {
return !(lhs == rhs);
}
atom_iterator<T> &operator++() {
_collection->next(_it);
return *this;
}
private:
const atom_collection<T> *_collection;
const void *_it;
};
/// \brief Must be implemented to return the atom_collection object for
/// all DefinedAtoms in this File.
virtual const atom_collection<DefinedAtom> &defined() const = 0;
/// \brief Must be implemented to return the atom_collection object for
/// all UndefinedAtomw in this File.
virtual const atom_collection<UndefinedAtom> &undefined() const = 0;
/// \brief Must be implemented to return the atom_collection object for
/// all SharedLibraryAtoms in this File.
virtual const atom_collection<SharedLibraryAtom> &sharedLibrary() const = 0;
/// \brief Must be implemented to return the atom_collection object for
/// all AbsoluteAtoms in this File.
virtual const atom_collection<AbsoluteAtom> &absolute() const = 0;
/// \brief If a file is parsed using a different method than doParse(),
/// one must use this method to set the last error status, so that
/// doParse will not be called twice. Only YAML reader uses this
/// (because YAML reader does not read blobs but structured data).
void setLastError(std::error_code err) { _lastError = err; }
std::error_code parse();
// This function is called just before the core linker tries to use
// a file. Currently the PECOFF reader uses this to trigger the
// driver to parse .drectve section (which contains command line options).
// If you want to do something having side effects, don't do that in
// doParse() because a file could be pre-loaded speculatively.
// Use this hook instead.
virtual void beforeLink() {}
// Usually each file owns a std::unique_ptr<MemoryBuffer>.
// However, there's one special case. If a file is an archive file,
// the archive file and its children all shares the same memory buffer.
// This method is used by the ArchiveFile to give its children
// co-ownership of the buffer.
void setSharedMemoryBuffer(std::shared_ptr<MemoryBuffer> mb) {
_sharedMemoryBuffer = mb;
}
protected:
/// \brief only subclasses of File can be instantiated
File(StringRef p, Kind kind)
: _path(p), _kind(kind), _ordinal(UINT64_MAX) {}
/// \brief Subclasses should override this method to parse the
/// memory buffer passed to this file's constructor.
virtual std::error_code doParse() { return std::error_code(); }
/// \brief This is a convenience class for File subclasses which manage their
/// atoms as a simple std::vector<>.
template <typename T>
class atom_collection_vector : public atom_collection<T> {
public:
atom_iterator<T> begin() const override {
auto *it = _atoms.empty() ? nullptr
: reinterpret_cast<const void *>(_atoms.data());
return atom_iterator<T>(*this, it);
}
atom_iterator<T> end() const override {
auto *it = _atoms.empty() ? nullptr : reinterpret_cast<const void *>(
_atoms.data() + _atoms.size());
return atom_iterator<T>(*this, it);
}
const T *deref(const void *it) const override {
return *reinterpret_cast<const T *const *>(it);
}
void next(const void *&it) const override {
const T *const *p = reinterpret_cast<const T *const *>(it);
++p;
it = reinterpret_cast<const void*>(p);
}
uint64_t size() const override { return _atoms.size(); }
std::vector<const T *> _atoms;
};
/// \brief This is a convenience class for File subclasses which need to
/// return an empty collection.
template <typename T>
class atom_collection_empty : public atom_collection<T> {
public:
atom_iterator<T> begin() const override {
return atom_iterator<T>(*this, nullptr);
}
atom_iterator<T> end() const override {
return atom_iterator<T>(*this, nullptr);
}
const T *deref(const void *it) const override {
llvm_unreachable("empty collection should never be accessed");
}
void next(const void *&it) const override {}
uint64_t size() const override { return 0; }
};
static atom_collection_empty<DefinedAtom> _noDefinedAtoms;
static atom_collection_empty<UndefinedAtom> _noUndefinedAtoms;
static atom_collection_empty<SharedLibraryAtom> _noSharedLibraryAtoms;
static atom_collection_empty<AbsoluteAtom> _noAbsoluteAtoms;
mutable llvm::BumpPtrAllocator _allocator;
private:
StringRef _path;
std::string _archivePath;
mutable std::string _archiveMemberPath;
Kind _kind;
mutable uint64_t _ordinal;
std::shared_ptr<MemoryBuffer> _sharedMemoryBuffer;
llvm::Optional<std::error_code> _lastError;
std::mutex _parseMutex;
};
/// \brief A mutable File.
class MutableFile : public File {
public:
/// \brief Add an atom to the file. Invalidates iterators for all returned
/// containters.
virtual void addAtom(const Atom&) = 0;
typedef range<std::vector<const DefinedAtom *>::iterator> DefinedAtomRange;
virtual DefinedAtomRange definedAtoms() = 0;
virtual void
removeDefinedAtomsIf(std::function<bool(const DefinedAtom *)> pred) = 0;
protected:
/// \brief only subclasses of MutableFile can be instantiated
MutableFile(StringRef p) : File(p, kindObject) {}
};
/// An ErrorFile represents a file that doesn't exist.
/// If you try to parse a file which doesn't exist, an instance of this
/// class will be returned. That's parse method always returns an error.
/// This is useful to delay erroring on non-existent files, so that we
/// can do unit testing a driver using non-existing file paths.
class ErrorFile : public File {
public:
ErrorFile(StringRef path, std::error_code ec)
: File(path, kindObject), _ec(ec) {}
std::error_code doParse() override { return _ec; }
const atom_collection<DefinedAtom> &defined() const override {
llvm_unreachable("internal error");
}
const atom_collection<UndefinedAtom> &undefined() const override {
llvm_unreachable("internal error");
}
const atom_collection<SharedLibraryAtom> &sharedLibrary() const override {
llvm_unreachable("internal error");
}
const atom_collection<AbsoluteAtom> &absolute() const override {
llvm_unreachable("internal error");
}
private:
std::error_code _ec;
};
} // end namespace lld
#endif

View file

@ -0,0 +1,132 @@
//===- include/Core/Instrumentation.h - Instrumentation API ---------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Provide an Instrumentation API that optionally uses VTune interfaces.
///
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_INSTRUMENTATION_H
#define LLD_CORE_INSTRUMENTATION_H
#include "llvm/Support/Compiler.h"
#include <utility>
#ifdef LLD_HAS_VTUNE
# include <ittnotify.h>
#endif
namespace lld {
#ifdef LLD_HAS_VTUNE
/// \brief A unique global scope for instrumentation data.
///
/// Domains last for the lifetime of the application and cannot be destroyed.
/// Multiple Domains created with the same name represent the same domain.
class Domain {
__itt_domain *_domain;
public:
explicit Domain(const char *name) : _domain(__itt_domain_createA(name)) {}
operator __itt_domain *() const { return _domain; }
__itt_domain *operator->() const { return _domain; }
};
/// \brief A global reference to a string constant.
///
/// These are uniqued by the ITT runtime and cannot be deleted. They are not
/// specific to a domain.
///
/// Prefer reusing a single StringHandle over passing a ntbs when the same
/// string will be used often.
class StringHandle {
__itt_string_handle *_handle;
public:
StringHandle(const char *name) : _handle(__itt_string_handle_createA(name)) {}
operator __itt_string_handle *() const { return _handle; }
};
/// \brief A task on a single thread. Nests within other tasks.
///
/// Each thread has its own task stack and tasks nest recursively on that stack.
/// A task cannot transfer threads.
///
/// SBRM is used to ensure task starts and ends are ballanced. The lifetime of
/// a task is either the lifetime of this object, or until end is called.
class ScopedTask {
__itt_domain *_domain;
ScopedTask(const ScopedTask &) = delete;
ScopedTask &operator=(const ScopedTask &) = delete;
public:
/// \brief Create a task in Domain \p d named \p s.
ScopedTask(const Domain &d, const StringHandle &s) : _domain(d) {
__itt_task_begin(d, __itt_null, __itt_null, s);
}
ScopedTask(ScopedTask &&other) {
*this = std::move(other);
}
ScopedTask &operator=(ScopedTask &&other) {
_domain = other._domain;
other._domain = nullptr;
return *this;
}
/// \brief Prematurely end this task.
void end() {
if (_domain)
__itt_task_end(_domain);
_domain = nullptr;
}
~ScopedTask() { end(); }
};
/// \brief A specific point in time. Allows metadata to be associated.
class Marker {
public:
Marker(const Domain &d, const StringHandle &s) {
__itt_marker(d, __itt_null, s, __itt_scope_global);
}
};
#else
class Domain {
public:
Domain(const char *name) {}
};
class StringHandle {
public:
StringHandle(const char *name) {}
};
class ScopedTask {
public:
ScopedTask(const Domain &d, const StringHandle &s) {}
void end() {}
};
class Marker {
public:
Marker(const Domain &d, const StringHandle &s) {}
};
#endif
inline const Domain &getDefaultDomain() {
static Domain domain("org.llvm.lld");
return domain;
}
} // end namespace lld.
#endif

75
include/lld/Core/LLVM.h Normal file
View file

@ -0,0 +1,75 @@
//===--- LLVM.h - Import various common LLVM datatypes ----------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file forward declares and imports various common LLVM datatypes that
// lld wants to use unqualified.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_LLVM_H
#define LLD_CORE_LLVM_H
// This should be the only #include, force #includes of all the others on
// clients.
#include "llvm/ADT/Hashing.h"
#include "llvm/Support/Casting.h"
#include <utility>
namespace llvm {
// ADT's.
class StringRef;
class Twine;
class MemoryBuffer;
template<typename T> class ArrayRef;
template<unsigned InternalLen> class SmallString;
template<typename T, unsigned N> class SmallVector;
template<typename T> class SmallVectorImpl;
template<typename T>
struct SaveAndRestore;
template<typename T>
class ErrorOr;
class raw_ostream;
// TODO: DenseMap, ...
}
namespace lld {
// Casting operators.
using llvm::isa;
using llvm::cast;
using llvm::dyn_cast;
using llvm::dyn_cast_or_null;
using llvm::cast_or_null;
// ADT's.
using llvm::StringRef;
using llvm::Twine;
using llvm::MemoryBuffer;
using llvm::ArrayRef;
using llvm::SmallString;
using llvm::SmallVector;
using llvm::SmallVectorImpl;
using llvm::SaveAndRestore;
using llvm::ErrorOr;
using llvm::raw_ostream;
} // end namespace lld.
namespace std {
template <> struct hash<llvm::StringRef> {
public:
size_t operator()(const llvm::StringRef &s) const {
return llvm::hash_value(s);
}
};
}
#endif

View file

@ -0,0 +1,364 @@
//===- lld/Core/LinkingContext.h - Linker Target Info Interface -----------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_LINKING_CONTEXT_H
#define LLD_CORE_LINKING_CONTEXT_H
#include "lld/Core/Error.h"
#include "lld/Core/LLVM.h"
#include "lld/Core/Node.h"
#include "lld/Core/Parallel.h"
#include "lld/Core/Reference.h"
#include "lld/Core/range.h"
#include "lld/Core/Reader.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/raw_ostream.h"
#include <string>
#include <vector>
namespace lld {
class PassManager;
class File;
class Writer;
class Node;
class SharedLibraryFile;
/// \brief The LinkingContext class encapsulates "what and how" to link.
///
/// The base class LinkingContext contains the options needed by core linking.
/// Subclasses of LinkingContext have additional options needed by specific
/// Writers. For example, ELFLinkingContext has methods that supplies
/// options to the ELF Writer and ELF Passes.
class LinkingContext {
public:
/// \brief The types of output file that the linker creates.
enum class OutputFileType : uint8_t {
Default, // The default output type for this target
YAML, // The output type is set to YAML
Native // The output file format is Native (Atoms)
};
virtual ~LinkingContext();
/// \name Methods needed by core linking
/// @{
/// Name of symbol linker should use as "entry point" to program,
/// usually "main" or "start".
virtual StringRef entrySymbolName() const { return _entrySymbolName; }
/// Whether core linking should remove Atoms not reachable by following
/// References from the entry point Atom or from all global scope Atoms
/// if globalsAreDeadStripRoots() is true.
bool deadStrip() const { return _deadStrip; }
/// Only used if deadStrip() returns true. Means all global scope Atoms
/// should be marked live (along with all Atoms they reference). Usually
/// this method returns false for main executables, but true for dynamic
/// shared libraries.
bool globalsAreDeadStripRoots() const { return _globalsAreDeadStripRoots; };
/// Only used if deadStrip() returns true. This method returns the names
/// of DefinedAtoms that should be marked live (along with all Atoms they
/// reference). Only Atoms with scope scopeLinkageUnit or scopeGlobal can
/// be kept live using this method.
const std::vector<StringRef> &deadStripRoots() const {
return _deadStripRoots;
}
/// Add the given symbol name to the dead strip root set. Only used if
/// deadStrip() returns true.
void addDeadStripRoot(StringRef symbolName) {
assert(!symbolName.empty() && "Empty symbol cannot be a dead strip root");
_deadStripRoots.push_back(symbolName);
}
/// Archive files (aka static libraries) are normally lazily loaded. That is,
/// object files within an archive are only loaded and linked in, if the
/// object file contains a DefinedAtom which will replace an existing
/// UndefinedAtom. If this method returns true, core linking will also look
/// for archive members to replace existing tentative definitions in addition
/// to replacing undefines. Note: a "tentative definition" (also called a
/// "common" symbols) is a C (but not C++) concept. They are modeled in lld
/// as a DefinedAtom with merge() of mergeAsTentative.
bool searchArchivesToOverrideTentativeDefinitions() const {
return _searchArchivesToOverrideTentativeDefinitions;
}
/// Normally core linking will turn a tentative definition into a real
/// definition if not replaced by a real DefinedAtom from some object file.
/// If this method returns true, core linking will search all supplied
/// dynamic shared libraries for symbol names that match remaining tentative
/// definitions. If any are found, the corresponding tentative definition
/// atom is replaced with SharedLibraryAtom.
bool searchSharedLibrariesToOverrideTentativeDefinitions() const {
return _searchSharedLibrariesToOverrideTentativeDefinitions;
}
/// Normally, every UndefinedAtom must be replaced by a DefinedAtom or a
/// SharedLibraryAtom for the link to be successful. This method controls
/// whether core linking prints out a list of remaining UndefinedAtoms.
///
/// \todo This should be a method core linking calls with a list of the
/// UndefinedAtoms so that different drivers can format the error message
/// as needed.
bool printRemainingUndefines() const { return _printRemainingUndefines; }
/// Normally, every UndefinedAtom must be replaced by a DefinedAtom or a
/// SharedLibraryAtom for the link to be successful. This method controls
/// whether core linking considers remaining undefines to be an error.
bool allowRemainingUndefines() const { return _allowRemainingUndefines; }
/// In the lld model, a SharedLibraryAtom is a proxy atom for something
/// that will be found in a dynamic shared library when the program runs.
/// A SharedLibraryAtom optionally contains the name of the shared library
/// in which to find the symbol name at runtime. Core linking may merge
/// two SharedLibraryAtom with the same name. If this method returns true,
/// when merging core linking will also verify that they both have the same
/// loadName() and if not print a warning.
///
/// \todo This should be a method core linking calls so that drivers can
/// format the warning as needed.
bool warnIfCoalesableAtomsHaveDifferentLoadName() const {
return _warnIfCoalesableAtomsHaveDifferentLoadName;
}
/// In C/C++ you can mark a function's prototype with
/// __attribute__((weak_import)) or __attribute__((weak)) to say the function
/// may not be available at runtime and/or build time and in which case its
/// address will evaluate to NULL. In lld this is modeled using the
/// UndefinedAtom::canBeNull() method. During core linking, UndefinedAtom
/// with the same name are automatically merged. If this method returns
/// true, core link also verfies that the canBeNull() value for merged
/// UndefinedAtoms are the same and warns if not.
///
/// \todo This should be a method core linking calls so that drivers can
/// format the warning as needed.
bool warnIfCoalesableAtomsHaveDifferentCanBeNull() const {
return _warnIfCoalesableAtomsHaveDifferentCanBeNull;
}
/// Normally, every UndefinedAtom must be replaced by a DefinedAtom or a
/// SharedLibraryAtom for the link to be successful. This method controls
/// whether core linking considers remaining undefines from the shared library
/// to be an error.
bool allowShlibUndefines() const { return _allowShlibUndefines; }
/// If true, core linking will write the path to each input file to stdout
/// (i.e. llvm::outs()) as it is used. This is used to implement the -t
/// linker option.
///
/// \todo This should be a method core linking calls so that drivers can
/// format the line as needed.
bool logInputFiles() const { return _logInputFiles; }
/// Parts of LLVM use global variables which are bound to command line
/// options (see llvm::cl::Options). This method returns "command line"
/// options which are used to configure LLVM's command line settings.
/// For instance the -debug-only XXX option can be used to dynamically
/// trace different parts of LLVM and lld.
const std::vector<const char *> &llvmOptions() const { return _llvmOptions; }
/// \name Methods used by Drivers to configure TargetInfo
/// @{
void setOutputPath(StringRef str) { _outputPath = str; }
// Set the entry symbol name. You may also need to call addDeadStripRoot() for
// the symbol if your platform supports dead-stripping, so that the symbol
// will not be removed from the output.
void setEntrySymbolName(StringRef name) {
_entrySymbolName = name;
}
void setDeadStripping(bool enable) { _deadStrip = enable; }
void setAllowDuplicates(bool enable) { _allowDuplicates = enable; }
void setGlobalsAreDeadStripRoots(bool v) { _globalsAreDeadStripRoots = v; }
void setSearchArchivesToOverrideTentativeDefinitions(bool search) {
_searchArchivesToOverrideTentativeDefinitions = search;
}
void setSearchSharedLibrariesToOverrideTentativeDefinitions(bool search) {
_searchSharedLibrariesToOverrideTentativeDefinitions = search;
}
void setWarnIfCoalesableAtomsHaveDifferentCanBeNull(bool warn) {
_warnIfCoalesableAtomsHaveDifferentCanBeNull = warn;
}
void setWarnIfCoalesableAtomsHaveDifferentLoadName(bool warn) {
_warnIfCoalesableAtomsHaveDifferentLoadName = warn;
}
void setPrintRemainingUndefines(bool print) {
_printRemainingUndefines = print;
}
void setAllowRemainingUndefines(bool allow) {
_allowRemainingUndefines = allow;
}
void setAllowShlibUndefines(bool allow) { _allowShlibUndefines = allow; }
void setLogInputFiles(bool log) { _logInputFiles = log; }
// Returns true if multiple definitions should not be treated as a
// fatal error.
bool getAllowDuplicates() const { return _allowDuplicates; }
void appendLLVMOption(const char *opt) { _llvmOptions.push_back(opt); }
void addAlias(StringRef from, StringRef to) { _aliasSymbols[from] = to; }
const std::map<std::string, std::string> &getAliases() const {
return _aliasSymbols;
}
std::vector<std::unique_ptr<Node>> &getNodes() { return _nodes; }
const std::vector<std::unique_ptr<Node>> &getNodes() const { return _nodes; }
/// Notify the LinkingContext when the symbol table found a name collision.
/// The useNew parameter specifies which the symbol table plans to keep,
/// but that can be changed by the LinkingContext. This is also an
/// opportunity for flavor specific processing.
virtual void notifySymbolTableCoalesce(const Atom *existingAtom,
const Atom *newAtom, bool &useNew) {}
/// This method adds undefined symbols specified by the -u option to the to
/// the list of undefined symbols known to the linker. This option essentially
/// forces an undefined symbol to be created. You may also need to call
/// addDeadStripRoot() for the symbol if your platform supports dead
/// stripping, so that the symbol will not be removed from the output.
void addInitialUndefinedSymbol(StringRef symbolName) {
_initialUndefinedSymbols.push_back(symbolName);
}
/// Iterators for symbols that appear on the command line.
typedef std::vector<StringRef> StringRefVector;
typedef StringRefVector::iterator StringRefVectorIter;
typedef StringRefVector::const_iterator StringRefVectorConstIter;
/// Create linker internal files containing atoms for the linker to include
/// during link. Flavors can override this function in their LinkingContext
/// to add more internal files. These internal files are positioned before
/// the actual input files.
virtual void createInternalFiles(std::vector<std::unique_ptr<File> > &) const;
/// Return the list of undefined symbols that are specified in the
/// linker command line, using the -u option.
range<const StringRef *> initialUndefinedSymbols() const {
return _initialUndefinedSymbols;
}
/// After all set* methods are called, the Driver calls this method
/// to validate that there are no missing options or invalid combinations
/// of options. If there is a problem, a description of the problem
/// is written to the supplied stream.
///
/// \returns true if there is an error with the current settings.
bool validate(raw_ostream &diagnostics);
/// Formats symbol name for use in error messages.
virtual std::string demangle(StringRef symbolName) const {
return symbolName;
}
/// @}
/// \name Methods used by Driver::link()
/// @{
/// Returns the file system path to which the linked output should be written.
///
/// \todo To support in-memory linking, we need an abstraction that allows
/// the linker to write to an in-memory buffer.
StringRef outputPath() const { return _outputPath; }
/// Set the various output file types that the linker would
/// create
bool setOutputFileType(StringRef outputFileType) {
if (outputFileType.equals_lower("yaml"))
_outputFileType = OutputFileType::YAML;
else if (outputFileType.equals_lower("native"))
_outputFileType = OutputFileType::YAML;
else
return false;
return true;
}
/// Returns the output file type that that the linker needs to create.
OutputFileType outputFileType() const { return _outputFileType; }
/// Accessor for Register object embedded in LinkingContext.
const Registry &registry() const { return _registry; }
Registry &registry() { return _registry; }
/// This method is called by core linking to give the Writer a chance
/// to add file format specific "files" to set of files to be linked. This is
/// how file format specific atoms can be added to the link.
virtual bool createImplicitFiles(std::vector<std::unique_ptr<File> > &);
/// This method is called by core linking to build the list of Passes to be
/// run on the merged/linked graph of all input files.
virtual void addPasses(PassManager &pm);
/// Calls through to the writeFile() method on the specified Writer.
///
/// \param linkedFile This is the merged/linked graph of all input file Atoms.
virtual std::error_code writeFile(const File &linkedFile) const;
/// Return the next ordinal and Increment it.
virtual uint64_t getNextOrdinalAndIncrement() const { return _nextOrdinal++; }
// This function is called just before the Resolver kicks in.
// Derived classes may use it to change the list of input files.
virtual void finalizeInputFiles() {}
TaskGroup &getTaskGroup() { return _taskGroup; }
/// @}
protected:
LinkingContext(); // Must be subclassed
/// Abstract method to lazily instantiate the Writer.
virtual Writer &writer() const = 0;
/// Method to create an internal file for the entry symbol
virtual std::unique_ptr<File> createEntrySymbolFile() const;
std::unique_ptr<File> createEntrySymbolFile(StringRef filename) const;
/// Method to create an internal file for an undefined symbol
virtual std::unique_ptr<File> createUndefinedSymbolFile() const;
std::unique_ptr<File> createUndefinedSymbolFile(StringRef filename) const;
/// Method to create an internal file for alias symbols
std::unique_ptr<File> createAliasSymbolFile() const;
StringRef _outputPath;
StringRef _entrySymbolName;
bool _deadStrip;
bool _allowDuplicates;
bool _globalsAreDeadStripRoots;
bool _searchArchivesToOverrideTentativeDefinitions;
bool _searchSharedLibrariesToOverrideTentativeDefinitions;
bool _warnIfCoalesableAtomsHaveDifferentCanBeNull;
bool _warnIfCoalesableAtomsHaveDifferentLoadName;
bool _printRemainingUndefines;
bool _allowRemainingUndefines;
bool _logInputFiles;
bool _allowShlibUndefines;
OutputFileType _outputFileType;
std::vector<StringRef> _deadStripRoots;
std::map<std::string, std::string> _aliasSymbols;
std::vector<const char *> _llvmOptions;
StringRefVector _initialUndefinedSymbols;
std::vector<std::unique_ptr<Node>> _nodes;
mutable llvm::BumpPtrAllocator _allocator;
mutable uint64_t _nextOrdinal;
Registry _registry;
private:
/// Validate the subclass bits. Only called by validate.
virtual bool validateImpl(raw_ostream &diagnostics) = 0;
TaskGroup _taskGroup;
};
} // end namespace lld
#endif

78
include/lld/Core/Node.h Normal file
View file

@ -0,0 +1,78 @@
//===- lld/Core/Node.h - Input file class ---------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// The classes in this file represents inputs to the linker.
///
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_NODE_H
#define LLD_CORE_NODE_H
#include "lld/Core/File.h"
#include "llvm/Option/ArgList.h"
#include <memory>
#include <vector>
namespace lld {
// A Node represents a FileNode or other type of Node. In the latter case,
// the node contains meta information about the input file list.
// Currently only GroupEnd node is defined as a meta node.
class Node {
public:
enum class Kind { File, GroupEnd };
explicit Node(Kind type) : _kind(type) {}
virtual ~Node() {}
virtual Kind kind() const { return _kind; }
private:
Kind _kind;
};
// This is a marker for --end-group. getSize() returns the number of
// files between the corresponding --start-group and this marker.
class GroupEnd : public Node {
public:
explicit GroupEnd(int size) : Node(Kind::GroupEnd), _size(size) {}
int getSize() const { return _size; }
static bool classof(const Node *a) {
return a->kind() == Kind::GroupEnd;
}
private:
int _size;
};
// A container of File.
class FileNode : public Node {
public:
explicit FileNode(std::unique_ptr<File> f)
: Node(Node::Kind::File), _file(std::move(f)), _asNeeded(false) {}
static bool classof(const Node *a) {
return a->kind() == Node::Kind::File;
}
File *getFile() { return _file.get(); }
void setAsNeeded(bool val) { _asNeeded = val; }
bool asNeeded() const { return _asNeeded; }
protected:
std::unique_ptr<File> _file;
bool _asNeeded;
};
} // namespace lld
#endif // LLD_CORE_NODE_H

309
include/lld/Core/Parallel.h Normal file
View file

@ -0,0 +1,309 @@
//===- lld/Core/Parallel.h - Parallel utilities ---------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_PARALLEL_H
#define LLD_CORE_PARALLEL_H
#include "lld/Core/Instrumentation.h"
#include "lld/Core/LLVM.h"
#include "lld/Core/range.h"
#include "llvm/Support/MathExtras.h"
#ifdef _MSC_VER
// concrt.h depends on eh.h for __uncaught_exception declaration
// even if we disable exceptions.
#include <eh.h>
#endif
#include <algorithm>
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <stack>
#ifdef _MSC_VER
#include <concrt.h>
#include <ppl.h>
#endif
namespace lld {
/// \brief Allows one or more threads to wait on a potentially unknown number of
/// events.
///
/// A latch starts at \p count. inc() increments this, and dec() decrements it.
/// All calls to sync() will block while the count is not 0.
///
/// Calling dec() on a Latch with a count of 0 has undefined behaivor.
class Latch {
uint32_t _count;
mutable std::mutex _condMut;
mutable std::condition_variable _cond;
public:
explicit Latch(uint32_t count = 0) : _count(count) {}
~Latch() { sync(); }
void inc() {
std::unique_lock<std::mutex> lock(_condMut);
++_count;
}
void dec() {
std::unique_lock<std::mutex> lock(_condMut);
if (--_count == 0)
_cond.notify_all();
}
void sync() const {
std::unique_lock<std::mutex> lock(_condMut);
_cond.wait(lock, [&] {
return _count == 0;
});
}
};
/// \brief An implementation of future. std::future and std::promise in
/// old libstdc++ have a threading bug; there is a small chance that a
/// call of future::get throws an exception in the normal use case.
/// We want to use our own future implementation until we drop support
/// of old versions of libstdc++.
/// https://gcc.gnu.org/ml/gcc-patches/2014-05/msg01389.html
template<typename T> class Future {
public:
Future() : _hasValue(false) {}
void set(T &&val) {
assert(!_hasValue);
{
std::unique_lock<std::mutex> lock(_mutex);
_val = val;
_hasValue = true;
}
_cond.notify_all();
}
T &get() {
std::unique_lock<std::mutex> lock(_mutex);
if (_hasValue)
return _val;
_cond.wait(lock, [&] { return _hasValue; });
return _val;
}
private:
T _val;
bool _hasValue;
std::mutex _mutex;
std::condition_variable _cond;
};
/// \brief An abstract class that takes closures and runs them asynchronously.
class Executor {
public:
virtual ~Executor() {}
virtual void add(std::function<void()> func) = 0;
};
/// \brief An implementation of an Executor that runs closures on a thread pool
/// in filo order.
class ThreadPoolExecutor : public Executor {
public:
explicit ThreadPoolExecutor(unsigned threadCount =
std::thread::hardware_concurrency())
: _stop(false), _done(threadCount) {
// Spawn all but one of the threads in another thread as spawning threads
// can take a while.
std::thread([&, threadCount] {
for (std::size_t i = 1; i < threadCount; ++i) {
std::thread([=] {
work();
}).detach();
}
work();
}).detach();
}
~ThreadPoolExecutor() {
std::unique_lock<std::mutex> lock(_mutex);
_stop = true;
lock.unlock();
_cond.notify_all();
// Wait for ~Latch.
}
void add(std::function<void()> f) override {
std::unique_lock<std::mutex> lock(_mutex);
_workStack.push(f);
lock.unlock();
_cond.notify_one();
}
private:
void work() {
while (true) {
std::unique_lock<std::mutex> lock(_mutex);
_cond.wait(lock, [&] {
return _stop || !_workStack.empty();
});
if (_stop)
break;
auto task = _workStack.top();
_workStack.pop();
lock.unlock();
task();
}
_done.dec();
}
std::atomic<bool> _stop;
std::stack<std::function<void()>> _workStack;
std::mutex _mutex;
std::condition_variable _cond;
Latch _done;
};
#ifdef _MSC_VER
/// \brief An Executor that runs tasks via ConcRT.
class ConcRTExecutor : public Executor {
struct Taskish {
Taskish(std::function<void()> task) : _task(task) {}
std::function<void()> _task;
static void run(void *p) {
Taskish *self = static_cast<Taskish *>(p);
self->_task();
concurrency::Free(self);
}
};
public:
virtual void add(std::function<void()> func) {
Concurrency::CurrentScheduler::ScheduleTask(Taskish::run,
new (concurrency::Alloc(sizeof(Taskish))) Taskish(func));
}
};
inline Executor *getDefaultExecutor() {
static ConcRTExecutor exec;
return &exec;
}
#else
inline Executor *getDefaultExecutor() {
static ThreadPoolExecutor exec;
return &exec;
}
#endif
/// \brief Allows launching a number of tasks and waiting for them to finish
/// either explicitly via sync() or implicitly on destruction.
class TaskGroup {
Latch _latch;
public:
void spawn(std::function<void()> f) {
_latch.inc();
getDefaultExecutor()->add([&, f] {
f();
_latch.dec();
});
}
void sync() const { _latch.sync(); }
};
#ifdef _MSC_VER
// Use ppl parallel_sort on Windows.
template <class RandomAccessIterator, class Comp>
void parallel_sort(
RandomAccessIterator start, RandomAccessIterator end,
const Comp &comp = std::less<
typename std::iterator_traits<RandomAccessIterator>::value_type>()) {
concurrency::parallel_sort(start, end, comp);
}
#else
namespace detail {
const ptrdiff_t minParallelSize = 1024;
/// \brief Inclusive median.
template <class RandomAccessIterator, class Comp>
RandomAccessIterator medianOf3(RandomAccessIterator start,
RandomAccessIterator end, const Comp &comp) {
RandomAccessIterator mid = start + (std::distance(start, end) / 2);
return comp(*start, *(end - 1))
? (comp(*mid, *(end - 1)) ? (comp(*start, *mid) ? mid : start)
: end - 1)
: (comp(*mid, *start) ? (comp(*(end - 1), *mid) ? mid : end - 1)
: start);
}
template <class RandomAccessIterator, class Comp>
void parallel_quick_sort(RandomAccessIterator start, RandomAccessIterator end,
const Comp &comp, TaskGroup &tg, size_t depth) {
// Do a sequential sort for small inputs.
if (std::distance(start, end) < detail::minParallelSize || depth == 0) {
std::sort(start, end, comp);
return;
}
// Partition.
auto pivot = medianOf3(start, end, comp);
// Move pivot to end.
std::swap(*(end - 1), *pivot);
pivot = std::partition(start, end - 1, [&comp, end](decltype(*start) v) {
return comp(v, *(end - 1));
});
// Move pivot to middle of partition.
std::swap(*pivot, *(end - 1));
// Recurse.
tg.spawn([=, &comp, &tg] {
parallel_quick_sort(start, pivot, comp, tg, depth - 1);
});
parallel_quick_sort(pivot + 1, end, comp, tg, depth - 1);
}
}
template <class RandomAccessIterator, class Comp>
void parallel_sort(
RandomAccessIterator start, RandomAccessIterator end,
const Comp &comp = std::less<
typename std::iterator_traits<RandomAccessIterator>::value_type>()) {
TaskGroup tg;
detail::parallel_quick_sort(start, end, comp, tg,
llvm::Log2_64(std::distance(start, end)) + 1);
}
#endif
template <class T> void parallel_sort(T *start, T *end) {
parallel_sort(start, end, std::less<T>());
}
#ifdef _MSC_VER
// Use ppl parallel_for_each on Windows.
template <class Iterator, class Func>
void parallel_for_each(Iterator begin, Iterator end, Func func) {
concurrency::parallel_for_each(begin, end, func);
}
#else
template <class Iterator, class Func>
void parallel_for_each(Iterator begin, Iterator end, Func func) {
TaskGroup tg;
ptrdiff_t taskSize = 1024;
while (taskSize <= std::distance(begin, end)) {
tg.spawn([=, &func] { std::for_each(begin, begin + taskSize, func); });
begin += taskSize;
}
std::for_each(begin, end, func);
}
#endif
} // end namespace lld
#endif

46
include/lld/Core/Pass.h Normal file
View file

@ -0,0 +1,46 @@
//===------ Core/Pass.h - Base class for linker passes --------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_PASS_H
#define LLD_CORE_PASS_H
#include "lld/Core/Atom.h"
#include "lld/Core/File.h"
#include "lld/Core/Reference.h"
#include "lld/Core/range.h"
#include <vector>
namespace lld {
class MutableFile;
/// Once the core linking is done (which resolves references, coalesces atoms
/// and produces a complete Atom graph), the linker runs a series of passes
/// on the Atom graph. The graph is modeled as a File, which means the pass
/// has access to all the atoms and to File level attributes. Each pass does
/// a particular transformation to the Atom graph or to the File attributes.
///
/// This is the abstract base class for all passes. A Pass does its
/// actual work in it perform() method. It can iterator over Atoms in the
/// graph using the *begin()/*end() atom iterator of the File. It can add
/// new Atoms to the graph using the File's addAtom() method.
class Pass {
public:
virtual ~Pass() { }
/// Do the actual work of the Pass.
virtual void perform(std::unique_ptr<MutableFile> &mergedFile) = 0;
protected:
// Only subclassess can be instantiated.
Pass() { }
};
} // namespace lld
#endif // LLD_CORE_PASS_H

View file

@ -0,0 +1,46 @@
//===- lld/Core/PassManager.h - Manage linker passes ----------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_PASS_MANAGER_H
#define LLD_CORE_PASS_MANAGER_H
#include "lld/Core/LLVM.h"
#include "lld/Core/Pass.h"
#include <memory>
#include <vector>
namespace lld {
class MutableFile;
class Pass;
/// \brief Owns and runs a collection of passes.
///
/// This class is currently just a container for passes and a way to run them.
///
/// In the future this should handle timing pass runs, running parallel passes,
/// and validate/satisfy pass dependencies.
class PassManager {
public:
void add(std::unique_ptr<Pass> pass) {
_passes.push_back(std::move(pass));
}
std::error_code runOnFile(std::unique_ptr<MutableFile> &file) {
for (std::unique_ptr<Pass> &pass : _passes)
pass->perform(file);
return std::error_code();
}
private:
/// \brief Passes in the order they should run.
std::vector<std::unique_ptr<Pass>> _passes;
};
} // end namespace lld
#endif

169
include/lld/Core/Reader.h Normal file
View file

@ -0,0 +1,169 @@
//===- lld/Core/Reader.h - Abstract File Format Reading Interface ---------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_READER_H
#define LLD_CORE_READER_H
#include "lld/Core/LLVM.h"
#include "lld/Core/Reference.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/YAMLTraits.h"
#include <functional>
#include <memory>
#include <vector>
using llvm::sys::fs::file_magic;
namespace llvm {
namespace yaml {
class IO;
}
}
namespace lld {
class ELFLinkingContext;
class File;
class LinkingContext;
class PECOFFLinkingContext;
class TargetHandlerBase;
class MachOLinkingContext;
/// \brief An abstract class for reading object files, library files, and
/// executable files.
///
/// Each file format (e.g. ELF, mach-o, PECOFF, native, etc) have a concrete
/// subclass of Reader.
class Reader {
public:
virtual ~Reader() {}
/// Sniffs the file to determine if this Reader can parse it.
/// The method is called with:
/// 1) the file_magic enumeration returned by identify_magic()
/// 2) the file extension (e.g. ".obj")
/// 3) the whole file content buffer if the above is not enough.
virtual bool canParse(file_magic magic, StringRef fileExtension,
const MemoryBuffer &mb) const = 0;
/// \brief Parse a supplied buffer (already filled with the contents of a
/// file) and create a File object.
/// The resulting File object takes ownership of the MemoryBuffer.
virtual std::error_code
loadFile(std::unique_ptr<MemoryBuffer> mb, const class Registry &,
std::vector<std::unique_ptr<File>> &result) const = 0;
};
/// \brief An abstract class for handling alternate yaml representations
/// of object files.
///
/// The YAML syntax allows "tags" which are used to specify the type of
/// the YAML node. In lld, top level YAML documents can be in many YAML
/// representations (e.g mach-o encoded as yaml, etc). A tag is used to
/// specify which representation is used in the following YAML document.
/// To work, there must be a YamlIOTaggedDocumentHandler registered that
/// handles each tag type.
class YamlIOTaggedDocumentHandler {
public:
virtual ~YamlIOTaggedDocumentHandler();
/// This method is called on each registered YamlIOTaggedDocumentHandler
/// until one returns true. If the subclass handles tag type !xyz, then
/// this method should call io.mapTag("!xzy") to see if that is the current
/// document type, and if so, process the rest of the document using
/// YAML I/O, then convert the result into an lld::File* and return it.
virtual bool handledDocTag(llvm::yaml::IO &io, const lld::File *&f) const = 0;
};
/// A registry to hold the list of currently registered Readers and
/// tables which map Reference kind values to strings.
/// The linker does not directly invoke Readers. Instead, it registers
/// Readers based on it configuration and command line options, then calls
/// the Registry object to parse files.
class Registry {
public:
Registry();
/// Walk the list of registered Readers and find one that can parse the
/// supplied file and parse it.
std::error_code loadFile(std::unique_ptr<MemoryBuffer> mb,
std::vector<std::unique_ptr<File>> &result) const;
/// Walk the list of registered kind tables to convert a Reference Kind
/// name to a value.
bool referenceKindFromString(StringRef inputStr, Reference::KindNamespace &ns,
Reference::KindArch &a,
Reference::KindValue &value) const;
/// Walk the list of registered kind tables to convert a Reference Kind
/// value to a string.
bool referenceKindToString(Reference::KindNamespace ns, Reference::KindArch a,
Reference::KindValue value, StringRef &) const;
/// Walk the list of registered tag handlers and have the one that handles
/// the current document type process the yaml into an lld::File*.
bool handleTaggedDoc(llvm::yaml::IO &io, const lld::File *&file) const;
// These methods are called to dynamically add support for various file
// formats. The methods are also implemented in the appropriate lib*.a
// library, so that the code for handling a format is only linked in, if this
// method is used. Any options that a Reader might need must be passed
// as parameters to the addSupport*() method.
void addSupportArchives(bool logLoading);
void addSupportYamlFiles();
void addSupportNativeObjects();
void addSupportCOFFObjects(PECOFFLinkingContext &);
void addSupportCOFFImportLibraries(PECOFFLinkingContext &);
void addSupportMachOObjects(MachOLinkingContext &);
void addSupportELFObjects(ELFLinkingContext &);
void addSupportELFDynamicSharedObjects(ELFLinkingContext &);
/// To convert between kind values and names, the registry walks the list
/// of registered kind tables. Each table is a zero terminated array of
/// KindStrings elements.
struct KindStrings {
Reference::KindValue value;
StringRef name;
};
/// A Reference Kind value is a tuple of <namespace, arch, value>. All
/// entries in a conversion table have the same <namespace, arch>. The
/// array then contains the value/name pairs.
void addKindTable(Reference::KindNamespace ns, Reference::KindArch arch,
const KindStrings array[]);
private:
struct KindEntry {
Reference::KindNamespace ns;
Reference::KindArch arch;
const KindStrings *array;
};
void add(std::unique_ptr<Reader>);
void add(std::unique_ptr<YamlIOTaggedDocumentHandler>);
std::vector<std::unique_ptr<Reader>> _readers;
std::vector<std::unique_ptr<YamlIOTaggedDocumentHandler>> _yamlHandlers;
std::vector<KindEntry> _kindEntries;
};
// Utilities for building a KindString table. For instance:
// static const Registry::KindStrings table[] = {
// LLD_KIND_STRING_ENTRY(R_VAX_ADDR16),
// LLD_KIND_STRING_ENTRY(R_VAX_DATA16),
// LLD_KIND_STRING_END
// };
#define LLD_KIND_STRING_ENTRY(name) { name, #name }
#define LLD_KIND_STRING_END { 0, "" }
} // end namespace lld
#endif

View file

@ -0,0 +1,125 @@
//===- Core/References.h - A Reference to Another Atom --------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_REFERENCES_H
#define LLD_CORE_REFERENCES_H
#include "lld/Core/LLVM.h"
#include "llvm/ADT/StringSwitch.h"
namespace lld {
class Atom;
///
/// The linker has a Graph Theory model of linking. An object file is seen
/// as a set of Atoms with References to other Atoms. Each Atom is a node
/// and each Reference is an edge.
///
/// For example if a function contains a call site to "malloc" 40 bytes into
/// the Atom, then the function Atom will have a Reference of: offsetInAtom=40,
/// kind=callsite, target=malloc, addend=0.
///
/// Besides supporting traditional "relocations", References are also used
/// grouping atoms (group comdat), forcing layout (one atom must follow
/// another), marking data-in-code (jump tables or ARM constants), etc.
///
/// The "kind" of a reference is a tuple of <namespace, arch, value>. This
/// enable us to re-use existing relocation types definded for various
/// file formats and architectures. For instance, in ELF the relocation type 10
/// means R_X86_64_32 for x86_64, and R_386_GOTPC for i386. For PE/COFF
/// relocation 10 means IMAGE_REL_AMD64_SECTION.
///
/// References and atoms form a directed graph. The dead-stripping pass
/// traverses them starting from dead-strip root atoms to garbage collect
/// unreachable ones.
///
/// References of any kind are considered as directed edges. In addition to
/// that, references of some kind is considered as bidirected edges.
class Reference {
public:
/// Which universe defines the kindValue().
enum class KindNamespace {
all = 0,
testing = 1,
ELF = 2,
COFF = 3,
mach_o = 4,
};
KindNamespace kindNamespace() const { return (KindNamespace)_kindNamespace; }
void setKindNamespace(KindNamespace ns) { _kindNamespace = (uint8_t)ns; }
// Which architecture the kind value is for.
enum class KindArch { all, AArch64, ARM, Hexagon, Mips, x86, x86_64 };
KindArch kindArch() const { return (KindArch)_kindArch; }
void setKindArch(KindArch a) { _kindArch = (uint8_t)a; }
typedef uint16_t KindValue;
KindValue kindValue() const { return _kindValue; }
/// setKindValue() is needed because during linking, some optimizations may
/// change the codegen and hence the reference kind.
void setKindValue(KindValue value) {
_kindValue = value;
}
/// KindValues used with KindNamespace::all and KindArch::all.
enum {
// kindLayoutAfter is treated as a bidirected edge by the dead-stripping
// pass.
kindLayoutAfter = 1,
// kindGroupChild is treated as a bidirected edge too.
kindGroupChild,
kindAssociate,
};
// A value to be added to the value of a target
typedef int64_t Addend;
/// If the reference is a fixup in the Atom, then this returns the
/// byte offset into the Atom's content to do the fix up.
virtual uint64_t offsetInAtom() const = 0;
/// Returns the atom this reference refers to.
virtual const Atom *target() const = 0;
/// During linking, the linker may merge graphs which coalesces some nodes
/// (i.e. Atoms). To switch the target of a reference, this method is called.
virtual void setTarget(const Atom *) = 0;
/// Some relocations require a symbol and a value (e.g. foo + 4).
virtual Addend addend() const = 0;
/// During linking, some optimzations may change addend value.
virtual void setAddend(Addend) = 0;
/// Returns target specific attributes of the reference.
virtual uint32_t tag() const { return 0; }
protected:
/// Reference is an abstract base class. Only subclasses can use constructor.
Reference(KindNamespace ns, KindArch a, KindValue value)
: _kindValue(value), _kindNamespace((uint8_t)ns), _kindArch((uint8_t)a) {}
/// The memory for Reference objects is always managed by the owning File
/// object. Therefore, no one but the owning File object should call
/// delete on an Reference. In fact, some File objects may bulk allocate
/// an array of References, so they cannot be individually deleted by anyone.
virtual ~Reference() {}
KindValue _kindValue;
uint8_t _kindNamespace;
uint8_t _kindArch;
};
} // namespace lld
#endif // LLD_CORE_REFERENCES_H

119
include/lld/Core/Resolver.h Normal file
View file

@ -0,0 +1,119 @@
//===- Core/Resolver.h - Resolves Atom References -------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_RESOLVER_H
#define LLD_CORE_RESOLVER_H
#include "lld/Core/ArchiveLibraryFile.h"
#include "lld/Core/File.h"
#include "lld/Core/SharedLibraryFile.h"
#include "lld/Core/Simple.h"
#include "lld/Core/SymbolTable.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace lld {
class Atom;
class LinkingContext;
/// \brief The Resolver is responsible for merging all input object files
/// and producing a merged graph.
class Resolver {
public:
Resolver(LinkingContext &ctx)
: _ctx(ctx), _symbolTable(ctx), _result(new MergedFile()),
_fileIndex(0) {}
// InputFiles::Handler methods
void doDefinedAtom(const DefinedAtom&);
bool doUndefinedAtom(const UndefinedAtom &);
void doSharedLibraryAtom(const SharedLibraryAtom &);
void doAbsoluteAtom(const AbsoluteAtom &);
// Handle files, this adds atoms from the current file thats
// being processed by the resolver
bool handleFile(File &);
// Handle an archive library file.
bool handleArchiveFile(File &);
// Handle a shared library file.
void handleSharedLibrary(File &);
/// @brief do work of merging and resolving and return list
bool resolve();
std::unique_ptr<MutableFile> resultFile() { return std::move(_result); }
private:
typedef std::function<void(StringRef, bool)> UndefCallback;
bool undefinesAdded(int begin, int end);
File *getFile(int &index);
/// \brief Add section group/.gnu.linkonce if it does not exist previously.
void maybeAddSectionGroupOrGnuLinkOnce(const DefinedAtom &atom);
/// \brief The main function that iterates over the files to resolve
void updatePreloadArchiveMap();
bool resolveUndefines();
void updateReferences();
void deadStripOptimize();
bool checkUndefines();
void removeCoalescedAwayAtoms();
void checkDylibSymbolCollisions();
void forEachUndefines(File &file, bool searchForOverrides, UndefCallback callback);
void markLive(const Atom *atom);
void addAtoms(const std::vector<const DefinedAtom *>&);
void maybePreloadArchiveMember(StringRef sym);
class MergedFile : public SimpleFile {
public:
MergedFile() : SimpleFile("<linker-internal>") {}
void addAtoms(std::vector<const Atom*>& atoms);
};
LinkingContext &_ctx;
SymbolTable _symbolTable;
std::vector<const Atom *> _atoms;
std::set<const Atom *> _deadStripRoots;
llvm::DenseSet<const Atom *> _liveAtoms;
llvm::DenseSet<const Atom *> _deadAtoms;
std::unique_ptr<MergedFile> _result;
std::unordered_multimap<const Atom *, const Atom *> _reverseRef;
// --start-group and --end-group
std::vector<File *> _files;
std::map<File *, bool> _newUndefinesAdded;
size_t _fileIndex;
// Preloading
llvm::StringMap<ArchiveLibraryFile *> _archiveMap;
llvm::DenseSet<ArchiveLibraryFile *> _archiveSeen;
// List of undefined symbols.
std::vector<StringRef> _undefines;
// Start position in _undefines for each archive/shared library file.
// Symbols from index 0 to the start position are already searched before.
// Searching them again would never succeed. When we look for undefined
// symbols from an archive/shared library file, start from its start
// position to save time.
std::map<File *, size_t> _undefineIndex;
};
} // namespace lld
#endif // LLD_CORE_RESOLVER_H

View file

@ -0,0 +1,29 @@
//===- lld/Core/STDExtra.h - Helpers for the stdlib -----------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_STD_EXTRA_H
#define LLD_CORE_STD_EXTRA_H
namespace lld {
/// \brief Deleter for smart pointers that only calls the destructor. Memory is
/// managed elsewhere. A common use of this is for things allocated with a
/// BumpPtrAllocator.
template <class T>
struct destruct_delete {
void operator ()(T *ptr) {
ptr->~T();
}
};
template <class T>
using unique_bump_ptr = std::unique_ptr<T, destruct_delete<T>>;
} // end namespace lld
#endif

View file

@ -0,0 +1,53 @@
//===- Core/SharedLibraryAtom.h - A Shared Library Atom -------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_SHARED_LIBRARY_ATOM_H
#define LLD_CORE_SHARED_LIBRARY_ATOM_H
#include "lld/Core/Atom.h"
namespace lld {
/// A SharedLibraryAtom has no content.
/// It exists to represent a symbol which will be bound at runtime.
class SharedLibraryAtom : public Atom {
public:
enum class Type : uint32_t {
Unknown,
Code,
Data,
};
/// Returns shared library name used to load it at runtime.
/// On linux that is the DT_NEEDED name.
/// On Darwin it is the LC_DYLIB_LOAD dylib name.
/// On Windows it is the DLL name that to be referred from .idata section.
virtual StringRef loadName() const = 0;
/// Returns if shared library symbol can be missing at runtime and if
/// so the loader should silently resolve address of symbol to be nullptr.
virtual bool canBeNullAtRuntime() const = 0;
virtual Type type() const = 0;
virtual uint64_t size() const = 0;
static bool classof(const Atom *a) {
return a->definition() == definitionSharedLibrary;
}
static inline bool classof(const SharedLibraryAtom *) { return true; }
protected:
SharedLibraryAtom() : Atom(definitionSharedLibrary) {}
};
} // namespace lld
#endif // LLD_CORE_SHARED_LIBRARY_ATOM_H

View file

@ -0,0 +1,65 @@
//===- Core/SharedLibraryFile.h - Models shared libraries as Atoms --------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_SHARED_LIBRARY_FILE_H
#define LLD_CORE_SHARED_LIBRARY_FILE_H
#include "lld/Core/File.h"
namespace lld {
///
/// The SharedLibraryFile subclass of File is used to represent dynamic
/// shared libraries being linked against.
///
class SharedLibraryFile : public File {
public:
static bool classof(const File *f) {
return f->kind() == kindSharedLibrary;
}
/// Check if the shared library exports a symbol with the specified name.
/// If so, return a SharedLibraryAtom which represents that exported
/// symbol. Otherwise return nullptr.
virtual const SharedLibraryAtom *exports(StringRef name,
bool dataSymbolOnly) const = 0;
// Returns DSO name. It's the soname (ELF), the install name (MachO) or
// the import name (Windows).
virtual StringRef getDSOName() const = 0;
const atom_collection<DefinedAtom> &defined() const override {
return _definedAtoms;
}
const atom_collection<UndefinedAtom> &undefined() const override {
return _undefinedAtoms;
}
const atom_collection<SharedLibraryAtom> &sharedLibrary() const override {
return _sharedLibraryAtoms;
}
const atom_collection<AbsoluteAtom> &absolute() const override {
return _absoluteAtoms;
}
protected:
/// only subclasses of SharedLibraryFile can be instantiated
explicit SharedLibraryFile(StringRef path) : File(path, kindSharedLibrary) {}
atom_collection_vector<DefinedAtom> _definedAtoms;
atom_collection_vector<UndefinedAtom> _undefinedAtoms;
atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms;
atom_collection_vector<AbsoluteAtom> _absoluteAtoms;
};
} // namespace lld
#endif // LLD_CORE_SHARED_LIBRARY_FILE_H

341
include/lld/Core/Simple.h Normal file
View file

@ -0,0 +1,341 @@
//===- lld/Core/Simple.h - Simple implementations of Atom and File --------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Provide simple implementations for Atoms and File.
///
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_SIMPLE_H
#define LLD_CORE_SIMPLE_H
#include "lld/Core/DefinedAtom.h"
#include "lld/Core/File.h"
#include "lld/Core/ArchiveLibraryFile.h"
#include "lld/Core/LinkingContext.h"
#include "lld/Core/Reference.h"
#include "lld/Core/UndefinedAtom.h"
#include "llvm/ADT/ilist.h"
#include "llvm/ADT/ilist_node.h"
namespace lld {
class SimpleFile : public MutableFile {
public:
SimpleFile(StringRef path) : MutableFile(path) {}
void addAtom(const Atom &atom) override {
if (auto *defAtom = dyn_cast<DefinedAtom>(&atom)) {
_definedAtoms._atoms.push_back(defAtom);
} else if (auto *undefAtom = dyn_cast<UndefinedAtom>(&atom)) {
_undefinedAtoms._atoms.push_back(undefAtom);
} else if (auto *shlibAtom = dyn_cast<SharedLibraryAtom>(&atom)) {
_sharedLibraryAtoms._atoms.push_back(shlibAtom);
} else if (auto *absAtom = dyn_cast<AbsoluteAtom>(&atom)) {
_absoluteAtoms._atoms.push_back(absAtom);
} else {
llvm_unreachable("atom has unknown definition kind");
}
}
void
removeDefinedAtomsIf(std::function<bool(const DefinedAtom *)> pred) override {
auto &atoms = _definedAtoms._atoms;
auto newEnd = std::remove_if(atoms.begin(), atoms.end(), pred);
atoms.erase(newEnd, atoms.end());
}
const atom_collection<DefinedAtom> &defined() const override {
return _definedAtoms;
}
const atom_collection<UndefinedAtom> &undefined() const override {
return _undefinedAtoms;
}
const atom_collection<SharedLibraryAtom> &sharedLibrary() const override {
return _sharedLibraryAtoms;
}
const atom_collection<AbsoluteAtom> &absolute() const override {
return _absoluteAtoms;
}
DefinedAtomRange definedAtoms() override {
return make_range(_definedAtoms._atoms);
}
private:
atom_collection_vector<DefinedAtom> _definedAtoms;
atom_collection_vector<UndefinedAtom> _undefinedAtoms;
atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms;
atom_collection_vector<AbsoluteAtom> _absoluteAtoms;
};
/// \brief Archive library file that may be used as a virtual container
/// for symbols that should be added dynamically in response to
/// call to find() method.
class SimpleArchiveLibraryFile : public ArchiveLibraryFile {
public:
SimpleArchiveLibraryFile(StringRef filename)
: ArchiveLibraryFile(filename) {}
const atom_collection<DefinedAtom> &defined() const override {
return _definedAtoms;
}
const atom_collection<UndefinedAtom> &undefined() const override {
return _undefinedAtoms;
}
const atom_collection<SharedLibraryAtom> &sharedLibrary() const override {
return _sharedLibraryAtoms;
}
const atom_collection<AbsoluteAtom> &absolute() const override {
return _absoluteAtoms;
}
File *find(StringRef sym, bool dataSymbolOnly) override {
// For descendants:
// do some checks here and return dynamically generated files with atoms.
return nullptr;
}
std::error_code
parseAllMembers(std::vector<std::unique_ptr<File>> &result) override {
return std::error_code();
}
private:
atom_collection_vector<DefinedAtom> _definedAtoms;
atom_collection_vector<UndefinedAtom> _undefinedAtoms;
atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms;
atom_collection_vector<AbsoluteAtom> _absoluteAtoms;
};
class SimpleReference : public Reference {
public:
SimpleReference(Reference::KindNamespace ns, Reference::KindArch arch,
Reference::KindValue value, uint64_t off, const Atom *t,
Reference::Addend a)
: Reference(ns, arch, value), _target(t), _offsetInAtom(off), _addend(a),
_next(nullptr), _prev(nullptr) {
}
SimpleReference()
: Reference(Reference::KindNamespace::all, Reference::KindArch::all, 0),
_target(nullptr), _offsetInAtom(0), _addend(0), _next(nullptr),
_prev(nullptr) {
}
uint64_t offsetInAtom() const override { return _offsetInAtom; }
const Atom *target() const override {
assert(_target);
return _target;
}
Addend addend() const override { return _addend; }
void setAddend(Addend a) override { _addend = a; }
void setTarget(const Atom *newAtom) override { _target = newAtom; }
SimpleReference *getNext() const { return _next; }
SimpleReference *getPrev() const { return _prev; }
void setNext(SimpleReference *n) { _next = n; }
void setPrev(SimpleReference *p) { _prev = p; }
private:
const Atom *_target;
uint64_t _offsetInAtom;
Addend _addend;
SimpleReference *_next;
SimpleReference *_prev;
};
}
// ilist will lazily create a sentinal (so end() can return a node past the
// end of the list). We need this trait so that the sentinal is allocated
// via the BumpPtrAllocator.
namespace llvm {
template<>
struct ilist_sentinel_traits<lld::SimpleReference> {
ilist_sentinel_traits() : _allocator(nullptr) { }
void setAllocator(llvm::BumpPtrAllocator *alloc) {
_allocator = alloc;
}
lld::SimpleReference *createSentinel() const {
return new (*_allocator) lld::SimpleReference();
}
static void destroySentinel(lld::SimpleReference*) {}
static lld::SimpleReference *provideInitialHead() { return nullptr; }
lld::SimpleReference *ensureHead(lld::SimpleReference *&head) const {
if (!head) {
head = createSentinel();
noteHead(head, head);
ilist_traits<lld::SimpleReference>::setNext(head, nullptr);
return head;
}
return ilist_traits<lld::SimpleReference>::getPrev(head);
}
void noteHead(lld::SimpleReference *newHead,
lld::SimpleReference *sentinel) const {
ilist_traits<lld::SimpleReference>::setPrev(newHead, sentinel);
}
private:
mutable llvm::BumpPtrAllocator *_allocator;
};
}
namespace lld {
class SimpleDefinedAtom : public DefinedAtom {
public:
explicit SimpleDefinedAtom(const File &f) : _file(f) {
static uint32_t lastOrdinal = 0;
_ordinal = lastOrdinal++;
_references.setAllocator(&f.allocator());
}
const File &file() const override { return _file; }
StringRef name() const override { return StringRef(); }
uint64_t ordinal() const override { return _ordinal; }
Scope scope() const override { return DefinedAtom::scopeLinkageUnit; }
Interposable interposable() const override {
return DefinedAtom::interposeNo;
}
Merge merge() const override { return DefinedAtom::mergeNo; }
Alignment alignment() const override { return Alignment(0, 0); }
SectionChoice sectionChoice() const override {
return DefinedAtom::sectionBasedOnContent;
}
StringRef customSectionName() const override { return StringRef(); }
DeadStripKind deadStrip() const override {
return DefinedAtom::deadStripNormal;
}
DefinedAtom::reference_iterator begin() const override {
const void *it = reinterpret_cast<const void *>(&*_references.begin());
return reference_iterator(*this, it);
}
DefinedAtom::reference_iterator end() const override {
const void *it = reinterpret_cast<const void *>(&*_references.end());
return reference_iterator(*this, it);
}
const Reference *derefIterator(const void *it) const override {
return reinterpret_cast<const Reference*>(it);
}
void incrementIterator(const void *&it) const override {
const SimpleReference* node = reinterpret_cast<const SimpleReference*>(it);
const SimpleReference* next = node->getNext();
it = reinterpret_cast<const void*>(next);
}
void addReference(Reference::KindNamespace ns, Reference::KindArch arch,
Reference::KindValue kindValue, uint64_t off,
const Atom *target, Reference::Addend a) {
assert(target && "trying to create reference to nothing");
auto node = new (_file.allocator())
SimpleReference(ns, arch, kindValue, off, target, a);
_references.push_back(node);
}
/// Sort references in a canonical order (by offset, then by kind).
void sortReferences() const {
// Cannot sort a linked list, so move elements into a temporary vector,
// sort the vector, then reconstruct the list.
llvm::SmallVector<SimpleReference *, 16> elements;
for (SimpleReference &node : _references) {
elements.push_back(&node);
}
std::sort(elements.begin(), elements.end(),
[] (const SimpleReference *lhs, const SimpleReference *rhs) -> bool {
uint64_t lhsOffset = lhs->offsetInAtom();
uint64_t rhsOffset = rhs->offsetInAtom();
if (rhsOffset != lhsOffset)
return (lhsOffset < rhsOffset);
if (rhs->kindNamespace() != lhs->kindNamespace())
return (lhs->kindNamespace() < rhs->kindNamespace());
if (rhs->kindArch() != lhs->kindArch())
return (lhs->kindArch() < rhs->kindArch());
return (lhs->kindValue() < rhs->kindValue());
});
_references.clearAndLeakNodesUnsafely();
for (SimpleReference *node : elements) {
_references.push_back(node);
}
}
void setOrdinal(uint64_t ord) { _ordinal = ord; }
private:
typedef llvm::ilist<SimpleReference> RefList;
const File &_file;
uint64_t _ordinal;
mutable RefList _references;
};
class SimpleUndefinedAtom : public UndefinedAtom {
public:
SimpleUndefinedAtom(const File &f, StringRef name) : _file(f), _name(name) {
assert(!name.empty() && "UndefinedAtoms must have a name");
}
/// file - returns the File that produced/owns this Atom
const File &file() const override { return _file; }
/// name - The name of the atom. For a function atom, it is the (mangled)
/// name of the function.
StringRef name() const override { return _name; }
CanBeNull canBeNull() const override { return UndefinedAtom::canBeNullNever; }
private:
const File &_file;
StringRef _name;
};
class SimpleAbsoluteAtom : public AbsoluteAtom {
public:
SimpleAbsoluteAtom(const File &f, StringRef name, Scope s, uint64_t value)
: _file(f), _name(name), _scope(s), _value(value) {}
const File &file() const override { return _file; }
StringRef name() const override { return _name; }
uint64_t value() const override { return _value; }
Scope scope() const override { return _scope; }
private:
const File &_file;
StringRef _name;
Scope _scope;
uint64_t _value;
};
} // end namespace lld
#endif

View file

@ -0,0 +1,117 @@
//===- Core/SymbolTable.h - Main Symbol Table -----------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_SYMBOL_TABLE_H
#define LLD_CORE_SYMBOL_TABLE_H
#include "lld/Core/LLVM.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/StringExtras.h"
#include <cstring>
#include <map>
#include <vector>
namespace lld {
class AbsoluteAtom;
class Atom;
class DefinedAtom;
class LinkingContext;
class ResolverOptions;
class SharedLibraryAtom;
class UndefinedAtom;
/// \brief The SymbolTable class is responsible for coalescing atoms.
///
/// All atoms coalescable by-name or by-content should be added.
/// The method replacement() can be used to find the replacement atom
/// if an atom has been coalesced away.
class SymbolTable {
public:
explicit SymbolTable(LinkingContext &);
/// @brief add atom to symbol table
bool add(const DefinedAtom &);
/// @brief add atom to symbol table
bool add(const UndefinedAtom &);
/// @brief add atom to symbol table
bool add(const SharedLibraryAtom &);
/// @brief add atom to symbol table
bool add(const AbsoluteAtom &);
/// @brief checks if name is in symbol table and if so atom is not
/// UndefinedAtom
bool isDefined(StringRef sym);
/// @brief returns atom in symbol table for specified name (or nullptr)
const Atom *findByName(StringRef sym);
/// @brief returns vector of remaining UndefinedAtoms
std::vector<const UndefinedAtom *> undefines();
/// returns vector of tentative definitions
std::vector<StringRef> tentativeDefinitions();
/// @brief add atom to replacement table
void addReplacement(const Atom *replaced, const Atom *replacement);
/// @brief if atom has been coalesced away, return replacement, else return atom
const Atom *replacement(const Atom *);
/// @brief if atom has been coalesced away, return true
bool isCoalescedAway(const Atom *);
/// @brief Find a group atom.
const Atom *findGroup(StringRef name);
/// @brief Add a group atom and returns true/false depending on whether the
/// previously existed.
bool addGroup(const DefinedAtom &da);
private:
typedef llvm::DenseMap<const Atom *, const Atom *> AtomToAtom;
struct StringRefMappingInfo {
static StringRef getEmptyKey() { return StringRef(); }
static StringRef getTombstoneKey() { return StringRef(" ", 1); }
static unsigned getHashValue(StringRef const val) {
return llvm::HashString(val);
}
static bool isEqual(StringRef const lhs, StringRef const rhs) {
return lhs.equals(rhs);
}
};
typedef llvm::DenseMap<StringRef, const Atom *,
StringRefMappingInfo> NameToAtom;
struct AtomMappingInfo {
static const DefinedAtom * getEmptyKey() { return nullptr; }
static const DefinedAtom * getTombstoneKey() { return (DefinedAtom*)(-1); }
static unsigned getHashValue(const DefinedAtom * const Val);
static bool isEqual(const DefinedAtom * const LHS,
const DefinedAtom * const RHS);
};
typedef llvm::DenseSet<const DefinedAtom*, AtomMappingInfo> AtomContentSet;
bool addByName(const Atom &);
bool addByContent(const DefinedAtom &);
LinkingContext &_context;
AtomToAtom _replacedAtoms;
NameToAtom _nameTable;
NameToAtom _groupTable;
AtomContentSet _contentTable;
};
} // namespace lld
#endif // LLD_CORE_SYMBOL_TABLE_H

17
include/lld/Core/TODO.txt Normal file
View file

@ -0,0 +1,17 @@
include/lld/Core
~~~~~~~~~~~~~~~~
* The native/yaml reader/writer interfaces should be changed to return
an explanatory string if there is an error. The existing error_code
abstraction only works for returning low level OS errors. It does not
work for describing formatting issues.
* We need to design a diagnostics interface. It would be nice to share code
with Clang_ where possible.
* We need to add more attributes to File. In particular, we need cpu
and OS information (like target triples). We should also provide explicit
support for `LLVM IR module flags metadata`__.
.. __: http://llvm.org/docs/LangRef.html#module_flags
.. _Clang: http://clang.llvm.org/docs/InternalsManual.html#Diagnostics

View file

@ -0,0 +1,74 @@
//===- Core/UndefinedAtom.h - An Undefined Atom ---------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_UNDEFINED_ATOM_H
#define LLD_CORE_UNDEFINED_ATOM_H
#include "lld/Core/Atom.h"
namespace lld {
/// An UndefinedAtom has no content.
/// It exists as a placeholder for a future atom.
class UndefinedAtom : public Atom {
public:
/// Whether this undefined symbol needs to be resolved,
/// or whether it can just evaluate to nullptr.
/// This concept is often called "weak", but that term
/// is overloaded to mean other things too.
enum CanBeNull {
/// Normal symbols must be resolved at build time
canBeNullNever,
/// This symbol can be missing at runtime and will evalute to nullptr.
/// That is, the static linker still must find a definition (usually
/// is some shared library), but at runtime, the dynamic loader
/// will allow the symbol to be missing and resolved to nullptr.
///
/// On Darwin this is generated using a function prototype with
/// __attribute__((weak_import)).
/// On linux this is generated using a function prototype with
/// __attribute__((weak)).
/// On Windows this feature is not supported.
canBeNullAtRuntime,
/// This symbol can be missing at build time.
/// That is, the static linker will not error if a definition for
/// this symbol is not found at build time. Instead, the linker
/// will build an executable that lets the dynamic loader find the
/// symbol at runtime.
/// This feature is not supported on Darwin nor Windows.
/// On linux this is generated using a function prototype with
/// __attribute__((weak)).
canBeNullAtBuildtime
};
virtual CanBeNull canBeNull() const = 0;
static bool classof(const Atom *a) {
return a->definition() == definitionUndefined;
}
static bool classof(const UndefinedAtom *) { return true; }
/// Returns an undefined atom if this undefined symbol has a synonym. This is
/// mainly used in COFF. In COFF, an unresolved external symbol can have up to
/// one optional name (sym2) in addition to its regular name (sym1). If a
/// definition of sym1 exists, sym1 is resolved normally. Otherwise, all
/// references to sym1 refer to sym2 instead. In that case sym2 must be
/// resolved, or link will fail.
virtual const UndefinedAtom *fallback() const { return nullptr; }
protected:
UndefinedAtom() : Atom(definitionUndefined) {}
};
} // namespace lld
#endif // LLD_CORE_UNDEFINED_ATOM_H

52
include/lld/Core/Writer.h Normal file
View file

@ -0,0 +1,52 @@
//===- lld/Core/Writer.h - Abstract File Format Interface -----------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_WRITER_H
#define LLD_CORE_WRITER_H
#include "lld/Core/LLVM.h"
#include <memory>
#include <vector>
namespace lld {
class File;
class ELFLinkingContext;
class MachOLinkingContext;
class PECOFFLinkingContext;
class LinkingContext;
class TargetHandlerBase;
/// \brief The Writer is an abstract class for writing object files, shared
/// library files, and executable files. Each file format (e.g. ELF, mach-o,
/// PECOFF, native, etc) have a concrete subclass of Writer.
class Writer {
public:
virtual ~Writer();
/// \brief Write a file from the supplied File object
virtual std::error_code writeFile(const File &linkedFile, StringRef path) = 0;
/// \brief This method is called by Core Linking to give the Writer a chance
/// to add file format specific "files" to set of files to be linked. This is
/// how file format specific atoms can be added to the link.
virtual bool createImplicitFiles(std::vector<std::unique_ptr<File> > &);
protected:
// only concrete subclasses can be instantiated
Writer();
};
std::unique_ptr<Writer> createWriterELF(TargetHandlerBase *handler);
std::unique_ptr<Writer> createWriterMachO(const MachOLinkingContext &);
std::unique_ptr<Writer> createWriterPECOFF(const PECOFFLinkingContext &);
std::unique_ptr<Writer> createWriterNative();
std::unique_ptr<Writer> createWriterYAML(const LinkingContext &);
} // end namespace lld
#endif

738
include/lld/Core/range.h Normal file
View file

@ -0,0 +1,738 @@
//===-- lld/Core/range.h - Iterator ranges ----------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Iterator range type based on c++1y range proposal.
///
/// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3350.html
///
//===----------------------------------------------------------------------===//
#ifndef LLD_CORE_RANGE_H
#define LLD_CORE_RANGE_H
#include "llvm/Support/Compiler.h"
#include <array>
#include <cassert>
#include <iterator>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
namespace lld {
// Nothing in this namespace is part of the exported interface.
namespace detail {
using std::begin;
using std::end;
/// Used as the result type of undefined functions.
struct undefined {};
template <typename R> class begin_result {
template <typename T> static auto check(T &&t) -> decltype(begin(t));
static undefined check(...);
public:
typedef decltype(check(std::declval<R>())) type;
};
template <typename R> class end_result {
template <typename T> static auto check(T &&t) -> decltype(end(t));
static undefined check(...);
public:
typedef decltype(check(std::declval<R>())) type;
};
// Things that begin and end work on, in compatible ways, are
// ranges. [stmt.ranged]
template <typename R>
struct is_range : std::is_same<typename detail::begin_result<R>::type,
typename detail::end_result<R>::type> {};
// This currently requires specialization and doesn't work for
// detecting \c range<>s or iterators. We should add
// \c contiguous_iterator_tag to fix that.
template <typename R> struct is_contiguous_range : std::false_type {};
template <typename R>
struct is_contiguous_range<R &> : is_contiguous_range<R> {};
template <typename R>
struct is_contiguous_range <R &&> : is_contiguous_range<R> {};
template <typename R>
struct is_contiguous_range<const R> : is_contiguous_range<R> {};
template <typename T, size_t N>
struct is_contiguous_range<T[N]> : std::true_type {};
template <typename T, size_t N>
struct is_contiguous_range<const T[N]> : std::true_type {};
template <typename T, size_t N>
struct is_contiguous_range<std::array<T, N> > : std::true_type {};
template <typename charT, typename traits, typename Allocator>
struct is_contiguous_range<
std::basic_string<charT, traits, Allocator> > : std::true_type {};
template <typename T, typename Allocator>
struct is_contiguous_range<std::vector<T, Allocator> > : std::true_type {};
// Removes cv qualifiers from all levels of a multi-level pointer
// type, not just the type level.
template <typename T> struct remove_all_cv_ptr {
typedef T type;
};
template <typename T> struct remove_all_cv_ptr<T *> {
typedef typename remove_all_cv_ptr<T>::type *type;
};
template <typename T> struct remove_all_cv_ptr<const T> {
typedef typename remove_all_cv_ptr<T>::type type;
};
template <typename T> struct remove_all_cv_ptr<volatile T> {
typedef typename remove_all_cv_ptr<T>::type type;
};
template <typename T> struct remove_all_cv_ptr<const volatile T> {
typedef typename remove_all_cv_ptr<T>::type type;
};
template <typename From, typename To>
struct conversion_preserves_array_indexing : std::false_type {};
template <typename FromVal, typename ToVal>
struct conversion_preserves_array_indexing<FromVal *,
ToVal *> : std::integral_constant<
bool, std::is_convertible<FromVal *, ToVal *>::value &&
std::is_same<typename remove_all_cv_ptr<FromVal>::type,
typename remove_all_cv_ptr<ToVal>::type>::value> {};
template <typename T>
LLVM_CONSTEXPR auto adl_begin(T &&t) -> decltype(begin(t)) {
return begin(std::forward<T>(t));
}
template <typename T> LLVM_CONSTEXPR auto adl_end(T &&t) -> decltype(end(t)) {
return end(std::forward<T>(t));
}
} // end namespace detail
/// A \c std::range<Iterator> represents a half-open iterator range
/// built from two iterators, \c 'begin', and \c 'end'. If \c end is
/// not reachable from \c begin, the behavior is undefined.
///
/// The mutability of elements of the range is controlled by the
/// Iterator argument. Instantiate
/// <code>range<<var>Foo</var>::iterator></code> or
/// <code>range<<var>T</var>*></code>, or call
/// <code>make_range(<var>non_const_container</var>)</code>, and you
/// get a mutable range. Instantiate
/// <code>range<<var>Foo</var>::const_iterator></code> or
/// <code>range<const <var>T</var>*></code>, or call
/// <code>make_range(<var>const_container</var>)</code>, and you get a
/// constant range.
///
/// \todo Inherit from std::pair<Iterator, Iterator>?
///
/// \todo This interface contains some functions that could be
/// provided as free algorithms rather than member functions, and all
/// of the <code>pop_*()</code> functions could be replaced by \c
/// slice() at the cost of some extra iterator copies. This makes
/// them more awkward to use, but makes it easier for users to write
/// their own types that follow the same interface. On the other hand,
/// a \c range_facade could be provided to help users write new
/// ranges, and it could provide the members. Such functions are
/// marked with a note in their documentation. (Of course, all of
/// these member functions could be provided as free functions using
/// the iterator access methods, but one goal here is to allow people
/// to program without touching iterators at all.)
template <typename Iterator> class range {
Iterator begin_, end_;
public:
/// \name types
/// @{
/// The iterator category of \c Iterator.
/// \todo Consider defining range categories. If they don't add
/// anything over the corresponding iterator categories, then
/// they're probably not worth defining.
typedef typename std::iterator_traits<
Iterator>::iterator_category iterator_category;
/// The type of elements of the range. Not cv-qualified.
typedef typename std::iterator_traits<Iterator>::value_type value_type;
/// The type of the size of the range and offsets within the range.
typedef typename std::iterator_traits<
Iterator>::difference_type difference_type;
/// The return type of element access methods: \c front(), \c back(), etc.
typedef typename std::iterator_traits<Iterator>::reference reference;
typedef typename std::iterator_traits<Iterator>::pointer pointer;
/// @}
/// \name constructors
/// @{
/// Creates a range of default-constructed (<em>not</em>
/// value-initialized) iterators. For most \c Iterator types, this
/// will be an invalid range.
range() : begin_(), end_() {}
/// \pre \c end is reachable from \c begin.
/// \post <code>this->begin() == begin && this->end() == end</code>
LLVM_CONSTEXPR range(Iterator begin, Iterator end)
: begin_(begin), end_(end) {}
/// \par Participates in overload resolution if:
/// - \c Iterator is not a pointer type,
/// - \c begin(r) and \c end(r) return the same type, and
/// - that type is convertible to \c Iterator.
///
/// \todo std::begin and std::end are overloaded between T& and
/// const T&, which means that if a container has only a non-const
/// begin or end method, then it's ill-formed to pass an rvalue to
/// the free function. To avoid that problem, we don't use
/// std::forward<> here, so begin() and end() are always called with
/// an lvalue. Another option would be to insist that rvalue
/// arguments to range() must have const begin() and end() methods.
template <typename R> LLVM_CONSTEXPR range(
R &&r,
typename std::enable_if<
!std::is_pointer<Iterator>::value &&
detail::is_range<R>::value &&
std::is_convertible<typename detail::begin_result<R>::type,
Iterator>::value>::type* = 0)
: begin_(detail::adl_begin(r)), end_(detail::adl_end(r)) {}
/// This constructor creates a \c range<T*> from any range with
/// contiguous iterators. Because dereferencing a past-the-end
/// iterator can be undefined behavior, empty ranges get initialized
/// with \c nullptr rather than \c &*begin().
///
/// \par Participates in overload resolution if:
/// - \c Iterator is a pointer type \c T*,
/// - \c begin(r) and \c end(r) return the same type,
/// - elements \c i of that type satisfy the invariant
/// <code>&*(i + N) == (&*i) + N</code>, and
/// - The result of <code>&*begin()</code> is convertible to \c T*
/// using only qualification conversions [conv.qual] (since
/// pointer conversions stop the pointer from pointing to an
/// array element).
///
/// \todo The <code>&*(i + N) == (&*i) + N</code> invariant is
/// currently impossible to check for user-defined types. We need a
/// \c contiguous_iterator_tag to let users assert it.
template <typename R> LLVM_CONSTEXPR range(
R &&r,
typename std::enable_if<
std::is_pointer<Iterator>::value &&
detail::is_contiguous_range<R>::value
// MSVC returns false for this in this context, but not if we lift it out of the
// constructor.
#ifndef _MSC_VER
&& detail::conversion_preserves_array_indexing<
decltype(&*detail::adl_begin(r)), Iterator>::value
#endif
>::type* = 0)
: begin_((detail::adl_begin(r) == detail::adl_end(r) &&
!std::is_pointer<decltype(detail::adl_begin(r))>::value)
// For non-pointers, &*begin(r) is only defined behavior
// if there's an element there. Otherwise, use nullptr
// since the user can't dereference it anyway. This _is_
// detectable.
? nullptr : &*detail::adl_begin(r)),
end_(begin_ + (detail::adl_end(r) - detail::adl_begin(r))) {}
/// @}
/// \name iterator access
/// @{
LLVM_CONSTEXPR Iterator begin() const { return begin_; }
LLVM_CONSTEXPR Iterator end() const { return end_; }
/// @}
/// \name element access
/// @{
/// \par Complexity:
/// O(1)
/// \pre \c !empty()
/// \returns a reference to the element at the front of the range.
LLVM_CONSTEXPR reference front() const { return *begin(); }
/// \par Ill-formed unless:
/// \c iterator_category is convertible to \c
/// std::bidirectional_iterator_tag.
///
/// \par Complexity:
/// O(2) (Involves copying and decrementing an iterator, so not
/// quite as cheap as \c front())
///
/// \pre \c !empty()
/// \returns a reference to the element at the front of the range.
LLVM_CONSTEXPR reference back() const {
static_assert(
std::is_convertible<iterator_category,
std::bidirectional_iterator_tag>::value,
"Can only retrieve the last element of a bidirectional range.");
using std::prev;
return *prev(end());
}
/// This method is drawn from scripting language indexing. It
/// indexes std::forward from the beginning of the range if the argument
/// is positive, or backwards from the end of the array if the
/// argument is negative.
///
/// \par Ill-formed unless:
/// \c iterator_category is convertible to \c
/// std::random_access_iterator_tag.
///
/// \par Complexity:
/// O(1)
///
/// \pre <code>abs(index) < size() || index == -size()</code>
///
/// \returns if <code>index >= 0</code>, a reference to the
/// <code>index</code>'th element in the range. Otherwise, a
/// reference to the <code>size()+index</code>'th element.
LLVM_CONSTEXPR reference operator[](difference_type index) const {
static_assert(std::is_convertible<iterator_category,
std::random_access_iterator_tag>::value,
"Can only index into a random-access range.");
// Less readable construction for constexpr support.
return index < 0 ? end()[index]
: begin()[index];
}
/// @}
/// \name size
/// @{
/// \par Complexity:
/// O(1)
/// \returns \c true if the range contains no elements.
LLVM_CONSTEXPR bool empty() const { return begin() == end(); }
/// \par Ill-formed unless:
/// \c iterator_category is convertible to
/// \c std::forward_iterator_tag.
///
/// \par Complexity:
/// O(1) if \c iterator_category is convertible to \c
/// std::random_access_iterator_tag. O(<code>size()</code>)
/// otherwise.
///
/// \returns the number of times \c pop_front() can be called before
/// \c empty() becomes true.
LLVM_CONSTEXPR difference_type size() const {
static_assert(std::is_convertible<iterator_category,
std::forward_iterator_tag>::value,
"Calling size on an input range would destroy the range.");
return dispatch_size(iterator_category());
}
/// @}
/// \name traversal from the beginning of the range
/// @{
/// Advances the beginning of the range by one element.
/// \pre \c !empty()
void pop_front() { ++begin_; }
/// Advances the beginning of the range by \c n elements.
///
/// \par Complexity:
/// O(1) if \c iterator_category is convertible to \c
/// std::random_access_iterator_tag, O(<code>n</code>) otherwise.
///
/// \pre <code>n >= 0</code>, and there must be at least \c n
/// elements in the range.
void pop_front(difference_type n) { advance(begin_, n); }
/// Advances the beginning of the range by at most \c n elements,
/// stopping if the range becomes empty. A negative argument causes
/// no change.
///
/// \par Complexity:
/// O(1) if \c iterator_category is convertible to \c
/// std::random_access_iterator_tag, O(<code>min(n,
/// <var>#-elements-in-range</var>)</code>) otherwise.
///
/// \note Could be provided as a free function with little-to-no
/// loss in efficiency.
void pop_front_upto(difference_type n) {
advance_upto(begin_, std::max<difference_type>(0, n), end_,
iterator_category());
}
/// @}
/// \name traversal from the end of the range
/// @{
/// Moves the end of the range earlier by one element.
///
/// \par Ill-formed unless:
/// \c iterator_category is convertible to
/// \c std::bidirectional_iterator_tag.
///
/// \par Complexity:
/// O(1)
///
/// \pre \c !empty()
void pop_back() {
static_assert(std::is_convertible<iterator_category,
std::bidirectional_iterator_tag>::value,
"Can only access the end of a bidirectional range.");
--end_;
}
/// Moves the end of the range earlier by \c n elements.
///
/// \par Ill-formed unless:
/// \c iterator_category is convertible to
/// \c std::bidirectional_iterator_tag.
///
/// \par Complexity:
/// O(1) if \c iterator_category is convertible to \c
/// std::random_access_iterator_tag, O(<code>n</code>) otherwise.
///
/// \pre <code>n >= 0</code>, and there must be at least \c n
/// elements in the range.
void pop_back(difference_type n) {
static_assert(std::is_convertible<iterator_category,
std::bidirectional_iterator_tag>::value,
"Can only access the end of a bidirectional range.");
advance(end_, -n);
}
/// Moves the end of the range earlier by <code>min(n,
/// size())</code> elements. A negative argument causes no change.
///
/// \par Ill-formed unless:
/// \c iterator_category is convertible to
/// \c std::bidirectional_iterator_tag.
///
/// \par Complexity:
/// O(1) if \c iterator_category is convertible to \c
/// std::random_access_iterator_tag, O(<code>min(n,
/// <var>#-elements-in-range</var>)</code>) otherwise.
///
/// \note Could be provided as a free function with little-to-no
/// loss in efficiency.
void pop_back_upto(difference_type n) {
static_assert(std::is_convertible<iterator_category,
std::bidirectional_iterator_tag>::value,
"Can only access the end of a bidirectional range.");
advance_upto(end_, -std::max<difference_type>(0, n), begin_,
iterator_category());
}
/// @}
/// \name creating derived ranges
/// @{
/// Divides the range into two pieces at \c index, where a positive
/// \c index represents an offset from the beginning of the range
/// and a negative \c index represents an offset from the end.
/// <code>range[index]</code> is the first element in the second
/// piece. If <code>index >= size()</code>, the second piece
/// will be empty. If <code>index < -size()</code>, the first
/// piece will be empty.
///
/// \par Ill-formed unless:
/// \c iterator_category is convertible to
/// \c std::forward_iterator_tag.
///
/// \par Complexity:
/// - If \c iterator_category is convertible to \c
/// std::random_access_iterator_tag: O(1)
/// - Otherwise, if \c iterator_category is convertible to \c
/// std::bidirectional_iterator_tag, \c abs(index) iterator increments
/// or decrements
/// - Otherwise, if <code>index >= 0</code>, \c index iterator
/// increments
/// - Otherwise, <code>size() + (size() + index)</code>
/// iterator increments.
///
/// \returns a pair of adjacent ranges.
///
/// \post
/// - <code>result.first.size() == min(index, this->size())</code>
/// - <code>result.first.end() == result.second.begin()</code>
/// - <code>result.first.size() + result.second.size()</code> <code>==
/// this->size()</code>
///
/// \todo split() could take an arbitrary number of indices and
/// return an <code>N+1</code>-element \c tuple<>. This is tricky to
/// implement with negative indices in the optimal number of
/// increments or decrements for a bidirectional iterator, but it
/// should be possible. Do we want it?
std::pair<range, range> split(difference_type index) const {
static_assert(
std::is_convertible<iterator_category,
std::forward_iterator_tag>::value,
"Calling split on a non-std::forward range would return a useless "
"first result.");
if (index >= 0) {
range second = *this;
second.pop_front_upto(index);
return make_pair(range(begin(), second.begin()), second);
} else {
return dispatch_split_neg(index, iterator_category());
}
}
/// \returns A sub-range from \c start to \c stop (not including \c
/// stop, as usual). \c start and \c stop are interpreted as for
/// <code>operator[]</code>, with negative values offsetting from
/// the end of the range. Omitting the \c stop argument makes the
/// sub-range continue to the end of the original range. Positive
/// arguments saturate to the end of the range, and negative
/// arguments saturate to the beginning. If \c stop is before \c
/// start, returns an empty range beginning and ending at \c start.
///
/// \par Ill-formed unless:
/// \c iterator_category is convertible to
/// \c std::forward_iterator_tag.
///
/// \par Complexity:
/// - If \c iterator_category is convertible to \c
/// std::random_access_iterator_tag: O(1)
/// - Otherwise, if \c iterator_category is convertible to \c
/// std::bidirectional_iterator_tag, at most <code>min(abs(start),
/// size()) + min(abs(stop), size())</code> iterator
/// increments or decrements
/// - Otherwise, if <code>start >= 0 && stop >= 0</code>,
/// <code>max(start, stop)</code> iterator increments
/// - Otherwise, <code>size() + max(start', stop')</code>
/// iterator increments, where \c start' and \c stop' are the
/// offsets of the elements \c start and \c stop refer to.
///
/// \note \c slice(start) should be implemented with a different
/// overload, rather than defaulting \c stop to
/// <code>numeric_limits<difference_type>::max()</code>, because
/// using a default would force non-random-access ranges to use an
/// O(<code>size()</code>) algorithm to compute the end rather
/// than the O(1) they're capable of.
range slice(difference_type start, difference_type stop) const {
static_assert(
std::is_convertible<iterator_category,
std::forward_iterator_tag>::value,
"Calling slice on a non-std::forward range would destroy the original "
"range.");
return dispatch_slice(start, stop, iterator_category());
}
range slice(difference_type start) const {
static_assert(
std::is_convertible<iterator_category,
std::forward_iterator_tag>::value,
"Calling slice on a non-std::forward range would destroy the original "
"range.");
return split(start).second;
}
/// @}
private:
// advance_upto: should be added to <algorithm>, but I'll use it as
// a helper function here.
//
// These return the number of increments that weren't applied
// because we ran into 'limit' (or 0 if we didn't run into limit).
static difference_type advance_upto(Iterator &it, difference_type n,
Iterator limit, std::input_iterator_tag) {
if (n < 0)
return 0;
while (it != limit && n > 0) {
++it;
--n;
}
return n;
}
static difference_type advance_upto(Iterator &it, difference_type n,
Iterator limit,
std::bidirectional_iterator_tag) {
if (n < 0) {
while (it != limit && n < 0) {
--it;
++n;
}
} else {
while (it != limit && n > 0) {
++it;
--n;
}
}
return n;
}
static difference_type advance_upto(Iterator &it, difference_type n,
Iterator limit,
std::random_access_iterator_tag) {
difference_type distance = limit - it;
if (distance < 0)
assert(n <= 0);
else if (distance > 0)
assert(n >= 0);
if (abs(distance) > abs(n)) {
it += n;
return 0;
} else {
it = limit;
return n - distance;
}
}
// Dispatch functions.
difference_type dispatch_size(std::forward_iterator_tag) const {
return std::distance(begin(), end());
}
LLVM_CONSTEXPR difference_type dispatch_size(
std::random_access_iterator_tag) const {
return end() - begin();
}
std::pair<range, range> dispatch_split_neg(difference_type index,
std::forward_iterator_tag) const {
assert(index < 0);
difference_type size = this->size();
return split(std::max<difference_type>(0, size + index));
}
std::pair<range, range> dispatch_split_neg(
difference_type index, std::bidirectional_iterator_tag) const {
assert(index < 0);
range first = *this;
first.pop_back_upto(-index);
return make_pair(first, range(first.end(), end()));
}
range dispatch_slice(difference_type start, difference_type stop,
std::forward_iterator_tag) const {
if (start < 0 || stop < 0) {
difference_type size = this->size();
if (start < 0)
start = std::max<difference_type>(0, size + start);
if (stop < 0)
stop = size + stop; // Possibly negative; will be fixed in 2 lines.
}
stop = std::max<difference_type>(start, stop);
Iterator first = begin();
advance_upto(first, start, end(), iterator_category());
Iterator last = first;
advance_upto(last, stop - start, end(), iterator_category());
return range(first, last);
}
range dispatch_slice(const difference_type start, const difference_type stop,
std::bidirectional_iterator_tag) const {
Iterator first;
if (start < 0) {
first = end();
advance_upto(first, start, begin(), iterator_category());
} else {
first = begin();
advance_upto(first, start, end(), iterator_category());
}
Iterator last;
if (stop < 0) {
last = end();
advance_upto(last, stop, first, iterator_category());
} else {
if (start >= 0) {
last = first;
if (stop > start)
advance_upto(last, stop - start, end(), iterator_category());
} else {
// Complicated: 'start' walked from the end of the sequence,
// but 'stop' needs to walk from the beginning.
Iterator dummy = begin();
// Walk up to 'stop' increments from begin(), stopping when we
// get to 'first', and capturing the remaining number of
// increments.
difference_type increments_past_start =
advance_upto(dummy, stop, first, iterator_category());
if (increments_past_start == 0) {
// If this is 0, then stop was before start.
last = first;
} else {
// Otherwise, count that many spaces beyond first.
last = first;
advance_upto(last, increments_past_start, end(), iterator_category());
}
}
}
return range(first, last);
}
range dispatch_slice(difference_type start, difference_type stop,
std::random_access_iterator_tag) const {
const difference_type size = this->size();
if (start < 0)
start = size + start;
if (start < 0)
start = 0;
if (start > size)
start = size;
if (stop < 0)
stop = size + stop;
if (stop < start)
stop = start;
if (stop > size)
stop = size;
return range(begin() + start, begin() + stop);
}
};
/// \name deducing constructor wrappers
/// \relates std::range
/// \xmlonly <nonmember/> \endxmlonly
///
/// These functions do the same thing as the constructor with the same
/// signature. They just allow users to avoid writing the iterator
/// type.
/// @{
/// \todo I'd like to define a \c make_range taking a single iterator
/// argument representing the beginning of a range that ends with a
/// default-constructed \c Iterator. This would help with using
/// iterators like \c istream_iterator. However, using just \c
/// make_range() could be confusing and lead to people writing
/// incorrect ranges of more common iterators. Is there a better name?
template <typename Iterator>
LLVM_CONSTEXPR range<Iterator> make_range(Iterator begin, Iterator end) {
return range<Iterator>(begin, end);
}
/// \par Participates in overload resolution if:
/// \c begin(r) and \c end(r) return the same type.
template <typename Range> LLVM_CONSTEXPR auto make_range(
Range &&r,
typename std::enable_if<detail::is_range<Range>::value>::type* = 0)
-> range<decltype(detail::adl_begin(r))> {
return range<decltype(detail::adl_begin(r))>(r);
}
/// \par Participates in overload resolution if:
/// - \c begin(r) and \c end(r) return the same type,
/// - that type satisfies the invariant that <code>&*(i + N) ==
/// (&*i) + N</code>, and
/// - \c &*begin(r) has a pointer type.
template <typename Range> LLVM_CONSTEXPR auto make_ptr_range(
Range &&r,
typename std::enable_if<
detail::is_contiguous_range<Range>::value &&
std::is_pointer<decltype(&*detail::adl_begin(r))>::value>::type* = 0)
-> range<decltype(&*detail::adl_begin(r))> {
return range<decltype(&*detail::adl_begin(r))>(r);
}
/// @}
} // end namespace lld
#endif

162
include/lld/Driver/Driver.h Normal file
View file

@ -0,0 +1,162 @@
//===- lld/Driver/Driver.h - Linker Driver Emulator -----------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// Interface for Drivers which convert command line arguments into
/// LinkingContext objects, then perform the link.
///
//===----------------------------------------------------------------------===//
#ifndef LLD_DRIVER_DRIVER_H
#define LLD_DRIVER_DRIVER_H
#include "lld/Core/LLVM.h"
#include "lld/Core/Node.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <set>
#include <vector>
namespace lld {
class LinkingContext;
class CoreLinkingContext;
class MachOLinkingContext;
class PECOFFLinkingContext;
class ELFLinkingContext;
typedef std::vector<std::unique_ptr<File>> FileVector;
FileVector makeErrorFile(StringRef path, std::error_code ec);
FileVector parseMemberFiles(FileVector &files);
FileVector loadFile(LinkingContext &ctx, StringRef path, bool wholeArchive);
/// Base class for all Drivers.
class Driver {
protected:
/// Performs link using specified options
static bool link(LinkingContext &context,
raw_ostream &diag = llvm::errs());
private:
Driver() = delete;
};
/// Driver for "universal" lld tool which can mimic any linker command line
/// parsing once it figures out which command line flavor to use.
class UniversalDriver : public Driver {
public:
/// Determine flavor and pass control to Driver for that flavor.
static bool link(int argc, const char *argv[],
raw_ostream &diag = llvm::errs());
private:
UniversalDriver() = delete;
};
/// Driver for gnu/binutil 'ld' command line options.
class GnuLdDriver : public Driver {
public:
/// Parses command line arguments same as gnu/binutils ld and performs link.
/// Returns true iff an error occurred.
static bool linkELF(int argc, const char *argv[],
raw_ostream &diag = llvm::errs());
/// Uses gnu/binutils style ld command line options to fill in options struct.
/// Returns true iff there was an error.
static bool parse(int argc, const char *argv[],
std::unique_ptr<ELFLinkingContext> &context,
raw_ostream &diag = llvm::errs());
/// Parses a given memory buffer as a linker script and evaluate that.
/// Public function for testing.
static std::error_code evalLinkerScript(ELFLinkingContext &ctx,
std::unique_ptr<MemoryBuffer> mb,
raw_ostream &diag, bool nostdlib);
/// A factory method to create an instance of ELFLinkingContext.
static std::unique_ptr<ELFLinkingContext>
createELFLinkingContext(llvm::Triple triple);
private:
static llvm::Triple getDefaultTarget(const char *progName);
static bool applyEmulation(llvm::Triple &triple,
llvm::opt::InputArgList &args,
raw_ostream &diag);
static void addPlatformSearchDirs(ELFLinkingContext &ctx,
llvm::Triple &triple,
llvm::Triple &baseTriple);
GnuLdDriver() = delete;
};
/// Driver for darwin/ld64 'ld' command line options.
class DarwinLdDriver : public Driver {
public:
/// Parses command line arguments same as darwin's ld and performs link.
/// Returns true iff there was an error.
static bool linkMachO(int argc, const char *argv[],
raw_ostream &diag = llvm::errs());
/// Uses darwin style ld command line options to update LinkingContext object.
/// Returns true iff there was an error.
static bool parse(int argc, const char *argv[], MachOLinkingContext &info,
raw_ostream &diag = llvm::errs());
private:
DarwinLdDriver() = delete;
};
/// Driver for Windows 'link.exe' command line options
class WinLinkDriver : public Driver {
public:
/// Parses command line arguments same as Windows link.exe and performs link.
/// Returns true iff there was an error.
static bool linkPECOFF(int argc, const char *argv[],
raw_ostream &diag = llvm::errs());
/// Uses Windows style link command line options to fill in options struct.
/// Returns true iff there was an error.
static bool parse(int argc, const char *argv[], PECOFFLinkingContext &info,
raw_ostream &diag = llvm::errs(),
bool isDirective = false);
// Same as parse(), but restricted to the context of directives.
static bool parseDirectives(int argc, const char *argv[],
PECOFFLinkingContext &info,
raw_ostream &diag = llvm::errs()) {
return parse(argc, argv, info, diag, true);
}
private:
WinLinkDriver() = delete;
};
/// Driver for lld unit tests
class CoreDriver : public Driver {
public:
/// Parses command line arguments same as lld-core and performs link.
/// Returns true iff there was an error.
static bool link(int argc, const char *argv[],
raw_ostream &diag = llvm::errs());
/// Uses lld-core command line options to fill in options struct.
/// Returns true iff there was an error.
static bool parse(int argc, const char *argv[], CoreLinkingContext &info,
raw_ostream &diag = llvm::errs());
private:
CoreDriver() = delete;
};
} // end namespace lld
#endif

View file

@ -0,0 +1,200 @@
//===- lld/Driver/WinLinkModuleDef.h --------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Windows module definition file parser.
///
//===----------------------------------------------------------------------===//
#ifndef LLD_DRIVER_WIN_LINK_MODULE_DEF_H
#define LLD_DRIVER_WIN_LINK_MODULE_DEF_H
#include "lld/Core/LLVM.h"
#include "lld/ReaderWriter/PECOFFLinkingContext.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Support/Allocator.h"
#include <vector>
namespace lld {
namespace moduledef {
enum class Kind {
unknown,
eof,
identifier,
comma,
equal,
kw_base,
kw_data,
kw_exports,
kw_heapsize,
kw_library,
kw_name,
kw_noname,
kw_private,
kw_stacksize,
kw_version,
};
class Token {
public:
Token() : _kind(Kind::unknown) {}
Token(Kind kind, StringRef range) : _kind(kind), _range(range) {}
Kind _kind;
StringRef _range;
};
class Lexer {
public:
explicit Lexer(std::unique_ptr<MemoryBuffer> mb) : _buffer(mb->getBuffer()) {
_sourceManager.AddNewSourceBuffer(std::move(mb), llvm::SMLoc());
}
Token lex();
const llvm::SourceMgr &getSourceMgr() const { return _sourceManager; }
private:
StringRef _buffer;
llvm::SourceMgr _sourceManager;
};
class Directive {
public:
enum class Kind { exports, heapsize, library, name, stacksize, version };
Kind getKind() const { return _kind; }
virtual ~Directive() {}
protected:
explicit Directive(Kind k) : _kind(k) {}
private:
Kind _kind;
};
class Exports : public Directive {
public:
explicit Exports(const std::vector<PECOFFLinkingContext::ExportDesc> &exports)
: Directive(Kind::exports), _exports(exports) {}
static bool classof(const Directive *dir) {
return dir->getKind() == Kind::exports;
}
const std::vector<PECOFFLinkingContext::ExportDesc> &getExports() const {
return _exports;
}
private:
const std::vector<PECOFFLinkingContext::ExportDesc> _exports;
};
template <Directive::Kind kind>
class MemorySize : public Directive {
public:
MemorySize(uint64_t reserve, uint64_t commit)
: Directive(kind), _reserve(reserve), _commit(commit) {}
static bool classof(const Directive *dir) {
return dir->getKind() == kind;
}
uint64_t getReserve() const { return _reserve; }
uint64_t getCommit() const { return _commit; }
private:
const uint64_t _reserve;
const uint64_t _commit;
};
typedef MemorySize<Directive::Kind::heapsize> Heapsize;
typedef MemorySize<Directive::Kind::stacksize> Stacksize;
class Name : public Directive {
public:
Name(StringRef outputPath, uint64_t baseaddr)
: Directive(Kind::name), _outputPath(outputPath), _baseaddr(baseaddr) {}
static bool classof(const Directive *dir) {
return dir->getKind() == Kind::name;
}
StringRef getOutputPath() const { return _outputPath; }
uint64_t getBaseAddress() const { return _baseaddr; }
private:
const std::string _outputPath;
const uint64_t _baseaddr;
};
class Library : public Directive {
public:
Library(StringRef name, uint64_t baseaddr)
: Directive(Kind::library), _name(name), _baseaddr(baseaddr) {}
static bool classof(const Directive *dir) {
return dir->getKind() == Kind::library;
}
StringRef getName() const { return _name; }
uint64_t getBaseAddress() const { return _baseaddr; }
private:
const std::string _name;
const uint64_t _baseaddr;
};
class Version : public Directive {
public:
Version(int major, int minor)
: Directive(Kind::version), _major(major), _minor(minor) {}
static bool classof(const Directive *dir) {
return dir->getKind() == Kind::version;
}
int getMajorVersion() const { return _major; }
int getMinorVersion() const { return _minor; }
private:
const int _major;
const int _minor;
};
class Parser {
public:
Parser(Lexer &lex, llvm::BumpPtrAllocator &alloc)
: _lex(lex), _alloc(alloc) {}
bool parse(std::vector<Directive *> &ret);
private:
void consumeToken();
bool consumeTokenAsInt(uint64_t &result);
bool expectAndConsume(Kind kind, Twine msg);
void ungetToken();
void error(const Token &tok, Twine msg);
bool parseOne(Directive *&dir);
bool parseExport(PECOFFLinkingContext::ExportDesc &result);
bool parseMemorySize(uint64_t &reserve, uint64_t &commit);
bool parseName(std::string &outfile, uint64_t &baseaddr);
bool parseVersion(int &major, int &minor);
Lexer &_lex;
llvm::BumpPtrAllocator &_alloc;
Token _tok;
std::vector<Token> _tokBuf;
};
}
}
#endif

44
include/lld/Makefile Normal file
View file

@ -0,0 +1,44 @@
LLD_LEVEL := ../..
DIRS := Config
include $(LLD_LEVEL)/Makefile
install-local::
$(Echo) Installing lld include files
$(Verb) $(MKDIR) $(DESTDIR)$(PROJ_includedir)
$(Verb) if test -d "$(PROJ_SRC_DIR)" ; then \
cd $(PROJ_SRC_DIR)/.. && \
for hdr in `find lld -type f \
'(' -name LICENSE.TXT \
-o -name '*.def' \
-o -name '*.h' \
-o -name '*.inc' \
')' -print \
| grep -v CVS | grep -v .svn | grep -v .dir` ; do \
instdir=$(DESTDIR)`dirname "$(PROJ_includedir)/$$hdr"` ; \
if test \! -d "$$instdir" ; then \
$(EchoCmd) Making install directory $$instdir ; \
$(MKDIR) $$instdir ;\
fi ; \
$(DataInstall) $$hdr $(DESTDIR)$(PROJ_includedir)/$$hdr ; \
done ; \
fi
ifneq ($(PROJ_SRC_ROOT),$(PROJ_OBJ_ROOT))
$(Verb) if test -d "$(PROJ_OBJ_ROOT)/tools/lld/include/lld" ; then \
cd $(PROJ_OBJ_ROOT)/tools/lld/include && \
for hdr in `find lld -type f \
'(' -name LICENSE.TXT \
-o -name '*.def' \
-o -name '*.h' \
-o -name '*.inc' \
')' -print \
| grep -v CVS | grep -v .tmp | grep -v .dir` ; do \
instdir=$(DESTDIR)`dirname "$(PROJ_includedir)/$$hdr"` ; \
if test \! -d "$$instdir" ; then \
$(EchoCmd) Making install directory $$instdir ; \
$(MKDIR) $$instdir ;\
fi ; \
$(DataInstall) $$hdr $(DESTDIR)$(PROJ_includedir)/$$hdr ; \
done ; \
fi
endif

View file

@ -0,0 +1,39 @@
//===- include/lld/ReaderWriter/AtomLayout.h ------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_READER_WRITER_ATOM_LAYOUT_H
#define LLD_READER_WRITER_ATOM_LAYOUT_H
namespace lld {
class Atom;
/// AtomLayouts are used by a writer to manage physical positions of atoms.
/// AtomLayout has two positions; one is file offset, and the other is the
/// address when loaded into memory.
///
/// Construction of AtomLayouts is usually a multi-pass process. When an atom
/// is appended to a section, we don't know the starting address of the
/// section. Thus, we have no choice but to store the offset from the
/// beginning of the section as AtomLayout values. After all sections starting
/// address are fixed, AtomLayout is revisited to get the offsets updated by
/// adding the starting addresses of the section.
struct AtomLayout {
AtomLayout(const Atom *a, uint64_t fileOff, uint64_t virAddr)
: _atom(a), _fileOffset(fileOff), _virtualAddr(virAddr) {}
AtomLayout() : _atom(nullptr), _fileOffset(0), _virtualAddr(0) {}
const Atom *_atom;
uint64_t _fileOffset;
uint64_t _virtualAddr;
};
}
#endif

View file

@ -0,0 +1,47 @@
//===- lld/ReaderWriter/CoreLinkingContext.h ------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_READER_WRITER_CORE_LINKER_CONTEXT_H
#define LLD_READER_WRITER_CORE_LINKER_CONTEXT_H
#include "lld/Core/LinkingContext.h"
#include "lld/Core/Reader.h"
#include "lld/Core/Writer.h"
#include "llvm/Support/ErrorHandling.h"
namespace lld {
class CoreLinkingContext : public LinkingContext {
public:
CoreLinkingContext();
enum {
TEST_RELOC_CALL32 = 1,
TEST_RELOC_PCREL32 = 2,
TEST_RELOC_GOT_LOAD32 = 3,
TEST_RELOC_GOT_USE32 = 4,
TEST_RELOC_LEA32_WAS_GOT = 5,
};
bool validateImpl(raw_ostream &diagnostics) override;
void addPasses(PassManager &pm) override;
void addPassNamed(StringRef name) { _passNames.push_back(name); }
protected:
Writer &writer() const override;
private:
std::unique_ptr<Writer> _writer;
std::vector<StringRef> _passNames;
};
} // end namespace lld
#endif

View file

@ -0,0 +1,362 @@
//===- lld/ReaderWriter/ELFLinkingContext.h -------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_READER_WRITER_ELF_LINKER_CONTEXT_H
#define LLD_READER_WRITER_ELF_LINKER_CONTEXT_H
#include "lld/Core/LinkingContext.h"
#include "lld/Core/Pass.h"
#include "lld/Core/PassManager.h"
#include "lld/Core/STDExtras.h"
#include "lld/Core/range.h"
#include "lld/Core/Reader.h"
#include "lld/Core/Writer.h"
#include "lld/ReaderWriter/LinkerScript.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/ELF.h"
#include <map>
#include <memory>
#include <set>
namespace lld {
class DefinedAtom;
class Reference;
class File;
namespace elf {
template <typename ELFT> class TargetHandler;
}
class TargetHandlerBase {
public:
virtual ~TargetHandlerBase() {}
virtual void registerRelocationNames(Registry &) = 0;
virtual std::unique_ptr<Reader> getObjReader() = 0;
virtual std::unique_ptr<Reader> getDSOReader() = 0;
virtual std::unique_ptr<Writer> getWriter() = 0;
};
class ELFLinkingContext : public LinkingContext {
public:
/// \brief The type of ELF executable that the linker
/// creates.
enum class OutputMagic : uint8_t {
DEFAULT, // The default mode, no specific magic set
NMAGIC, // Disallow shared libraries and don't align sections
// PageAlign Data, Mark Text Segment/Data segment RW
OMAGIC // Disallow shared libraries and don't align sections,
// Mark Text Segment/Data segment RW
};
llvm::Triple getTriple() const { return _triple; }
// Page size.
virtual uint64_t getPageSize() const {
if (_maxPageSize)
return *_maxPageSize;
return 0x1000;
}
virtual void setMaxPageSize(uint64_t pagesize) {
_maxPageSize = pagesize;
}
OutputMagic getOutputMagic() const { return _outputMagic; }
uint16_t getOutputELFType() const { return _outputELFType; }
uint16_t getOutputMachine() const;
bool mergeCommonStrings() const { return _mergeCommonStrings; }
virtual uint64_t getBaseAddress() const { return _baseAddress; }
virtual void setBaseAddress(uint64_t address) { _baseAddress = address; }
void notifySymbolTableCoalesce(const Atom *existingAtom, const Atom *newAtom,
bool &useNew) override;
/// This controls if undefined atoms need to be created for undefines that are
/// present in a SharedLibrary. If this option is set, undefined atoms are
/// created for every undefined symbol that are present in the dynamic table
/// in the shared library
bool useShlibUndefines() const { return _useShlibUndefines; }
/// @}
/// \brief Does this relocation belong in the dynamic relocation table?
///
/// This table is evaluated at loadtime by the dynamic loader and is
/// referenced by the DT_RELA{,ENT,SZ} entries in the dynamic table.
/// Relocations that return true will be added to the dynamic relocation
/// table.
virtual bool isDynamicRelocation(const Reference &) const { return false; }
/// \brief Is this a copy relocation?
///
/// If this is a copy relocation, its target must be an ObjectAtom. We must
/// include in DT_NEEDED the name of the library where this object came from.
virtual bool isCopyRelocation(const Reference &) const {
return false;
}
bool validateImpl(raw_ostream &diagnostics) override;
/// \brief Does the linker allow dynamic libraries to be linked with?
/// This is true when the output mode of the executable is set to be
/// having NMAGIC/OMAGIC
virtual bool allowLinkWithDynamicLibraries() const {
if (_outputMagic == OutputMagic::NMAGIC ||
_outputMagic == OutputMagic::OMAGIC || _noAllowDynamicLibraries)
return false;
return true;
}
/// \brief Use Elf_Rela format to output relocation tables.
virtual bool isRelaOutputFormat() const { return true; }
/// \brief Does this relocation belong in the dynamic plt relocation table?
///
/// This table holds all of the relocations used for delayed symbol binding.
/// It will be evaluated at load time if LD_BIND_NOW is set. It is referenced
/// by the DT_{JMPREL,PLTRELSZ} entries in the dynamic table.
/// Relocations that return true will be added to the dynamic plt relocation
/// table.
virtual bool isPLTRelocation(const Reference &) const { return false; }
/// \brief The path to the dynamic interpreter
virtual StringRef getDefaultInterpreter() const {
return "/lib64/ld-linux-x86-64.so.2";
}
/// \brief The dynamic linker path set by the --dynamic-linker option
virtual StringRef getInterpreter() const {
if (_dynamicLinkerArg)
return _dynamicLinkerPath;
return getDefaultInterpreter();
}
/// \brief Does the output have dynamic sections.
virtual bool isDynamic() const;
/// \brief Are we creating a shared library?
virtual bool isDynamicLibrary() const {
return _outputELFType == llvm::ELF::ET_DYN;
}
/// \brief Is the relocation a relative relocation
virtual bool isRelativeReloc(const Reference &r) const;
template <typename ELFT>
lld::elf::TargetHandler<ELFT> &getTargetHandler() const {
assert(_targetHandler && "Got null TargetHandler!");
return static_cast<lld::elf::TargetHandler<ELFT> &>(*_targetHandler.get());
}
TargetHandlerBase *targetHandler() const { return _targetHandler.get(); }
void addPasses(PassManager &pm) override;
void setTriple(llvm::Triple trip) { _triple = trip; }
void setNoInhibitExec(bool v) { _noInhibitExec = v; }
void setExportDynamic(bool v) { _exportDynamic = v; }
void setIsStaticExecutable(bool v) { _isStaticExecutable = v; }
void setMergeCommonStrings(bool v) { _mergeCommonStrings = v; }
void setUseShlibUndefines(bool use) { _useShlibUndefines = use; }
void setOutputELFType(uint32_t type) { _outputELFType = type; }
bool shouldExportDynamic() const { return _exportDynamic; }
void createInternalFiles(std::vector<std::unique_ptr<File>> &) const override;
void finalizeInputFiles() override;
/// \brief Set the dynamic linker path
void setInterpreter(StringRef dynamicLinker) {
_dynamicLinkerArg = true;
_dynamicLinkerPath = dynamicLinker;
}
/// \brief Set NMAGIC output kind when the linker specifies --nmagic
/// or -n in the command line
/// Set OMAGIC output kind when the linker specifies --omagic
/// or -N in the command line
virtual void setOutputMagic(OutputMagic magic) { _outputMagic = magic; }
/// \brief Disallow dynamic libraries during linking
virtual void setNoAllowDynamicLibraries() { _noAllowDynamicLibraries = true; }
/// Searches directories for a match on the input File
ErrorOr<StringRef> searchLibrary(StringRef libName) const;
/// \brief Searches directories for a match on the input file.
/// If \p fileName is an absolute path and \p isSysRooted is true, check
/// the file under sysroot directory. If \p fileName is a relative path
/// and is not in the current directory, search the file through library
/// search directories.
ErrorOr<StringRef> searchFile(StringRef fileName, bool isSysRooted) const;
/// Get the entry symbol name
StringRef entrySymbolName() const override;
/// \brief Set new initializer function
void setInitFunction(StringRef name) { _initFunction = name; }
/// \brief Return an initializer function name.
/// Either default "_init" or configured by the -init command line option.
StringRef initFunction() const { return _initFunction; }
/// \brief Set new finalizer function
void setFiniFunction(StringRef name) { _finiFunction = name; }
/// \brief Return a finalizer function name.
/// Either default "_fini" or configured by the -fini command line option.
StringRef finiFunction() const { return _finiFunction; }
/// Add an absolute symbol. Used for --defsym.
void addInitialAbsoluteSymbol(StringRef name, uint64_t addr) {
_absoluteSymbols[name] = addr;
}
void setSharedObjectName(StringRef soname) {
_soname = soname;
}
StringRef sharedObjectName() const { return _soname; }
StringRef getSysroot() const { return _sysrootPath; }
/// \brief Set path to the system root
void setSysroot(StringRef path) {
_sysrootPath = path;
}
void addRpath(StringRef path) {
_rpathList.push_back(path);
}
range<const StringRef *> getRpathList() const {
return _rpathList;
}
void addRpathLink(StringRef path) {
_rpathLinkList.push_back(path);
}
range<const StringRef *> getRpathLinkList() const {
return _rpathLinkList;
}
const std::map<std::string, uint64_t> &getAbsoluteSymbols() const {
return _absoluteSymbols;
}
/// \brief Helper function to allocate strings.
StringRef allocateString(StringRef ref) const {
char *x = _allocator.Allocate<char>(ref.size() + 1);
memcpy(x, ref.data(), ref.size());
x[ref.size()] = '\0';
return x;
}
// add search path to list.
virtual bool addSearchPath(StringRef ref) {
_inputSearchPaths.push_back(ref);
return true;
}
// Retrieve search path list.
StringRefVector getSearchPaths() { return _inputSearchPaths; };
// By default, the linker would merge sections that are read only with
// segments that have read and execute permissions. When the user specifies a
// flag --rosegment, a separate segment needs to be created.
bool mergeRODataToTextSegment() const { return _mergeRODataToTextSegment; }
void setCreateSeparateROSegment() { _mergeRODataToTextSegment = false; }
bool isDynamicallyExportedSymbol(StringRef name) const {
return _dynamicallyExportedSymbols.count(name) != 0;
}
/// \brief Demangle symbols.
std::string demangle(StringRef symbolName) const override;
bool demangleSymbols() const { return _demangle; }
void setDemangleSymbols(bool d) { _demangle = d; }
/// \brief Align segments.
bool alignSegments() const { return _alignSegments; }
void setAlignSegments(bool align) { _alignSegments = align; }
/// \brief Strip symbols.
bool stripSymbols() const { return _stripSymbols; }
void setStripSymbols(bool strip) { _stripSymbols = strip; }
/// \brief Collect statistics.
bool collectStats() const { return _collectStats; }
void setCollectStats(bool s) { _collectStats = s; }
// --wrap option.
void addWrapForSymbol(StringRef sym) { _wrapCalls.insert(sym); }
const llvm::StringSet<> &wrapCalls() const { return _wrapCalls; }
void setUndefinesResolver(std::unique_ptr<File> resolver);
script::Sema &linkerScriptSema() { return _linkerScriptSema; }
const script::Sema &linkerScriptSema() const { return _linkerScriptSema; }
private:
ELFLinkingContext() = delete;
protected:
ELFLinkingContext(llvm::Triple, std::unique_ptr<TargetHandlerBase>);
Writer &writer() const override;
/// Method to create a internal file for an undefined symbol
std::unique_ptr<File> createUndefinedSymbolFile() const override;
uint16_t _outputELFType; // e.g ET_EXEC
llvm::Triple _triple;
std::unique_ptr<TargetHandlerBase> _targetHandler;
uint64_t _baseAddress;
bool _isStaticExecutable;
bool _noInhibitExec;
bool _exportDynamic;
bool _mergeCommonStrings;
bool _useShlibUndefines;
bool _dynamicLinkerArg;
bool _noAllowDynamicLibraries;
bool _mergeRODataToTextSegment;
bool _demangle;
bool _stripSymbols;
bool _alignSegments;
bool _nostdlib;
bool _collectStats;
llvm::Optional<uint64_t> _maxPageSize;
OutputMagic _outputMagic;
StringRefVector _inputSearchPaths;
std::unique_ptr<Writer> _writer;
StringRef _dynamicLinkerPath;
StringRef _initFunction;
StringRef _finiFunction;
StringRef _sysrootPath;
StringRef _soname;
StringRefVector _rpathList;
StringRefVector _rpathLinkList;
llvm::StringSet<> _wrapCalls;
std::map<std::string, uint64_t> _absoluteSymbols;
llvm::StringSet<> _dynamicallyExportedSymbols;
std::unique_ptr<File> _resolver;
// The linker script semantic object, which owns all script ASTs, is stored
// in the current linking context via _linkerScriptSema.
script::Sema _linkerScriptSema;
};
} // end namespace lld
#endif

View file

@ -0,0 +1,38 @@
//===- lld/ReaderWriter/ELFTargets.h --------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_READER_WRITER_ELF_TARGETS_H
#define LLD_READER_WRITER_ELF_TARGETS_H
#include "ELFLinkingContext.h"
namespace lld {
namespace elf {
#define LLVM_TARGET(TargetName) \
class TargetName##LinkingContext final : public ELFLinkingContext { \
public: \
static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); \
};
// FIXME: #include "llvm/Config/Targets.def"
LLVM_TARGET(AArch64)
LLVM_TARGET(ARM)
LLVM_TARGET(Hexagon)
LLVM_TARGET(Mips)
LLVM_TARGET(X86)
LLVM_TARGET(Example)
LLVM_TARGET(X86_64)
#undef LLVM_TARGET
} // end namespace elf
} // end namespace lld
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,369 @@
//===- lld/ReaderWriter/MachOLinkingContext.h -----------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_READER_WRITER_MACHO_LINKING_CONTEXT_H
#define LLD_READER_WRITER_MACHO_LINKING_CONTEXT_H
#include "lld/Core/LinkingContext.h"
#include "lld/Core/Reader.h"
#include "lld/Core/Writer.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MachO.h"
#include <set>
using llvm::MachO::HeaderFileType;
namespace lld {
namespace mach_o {
class ArchHandler;
class MachODylibFile;
class MachOFile;
}
class MachOLinkingContext : public LinkingContext {
public:
MachOLinkingContext();
~MachOLinkingContext();
enum Arch {
arch_unknown,
arch_ppc,
arch_x86,
arch_x86_64,
arch_armv6,
arch_armv7,
arch_armv7s,
arch_arm64,
};
enum class OS {
unknown,
macOSX,
iOS,
iOS_simulator
};
enum class ExportMode {
globals, // Default, all global symbols exported.
whiteList, // -exported_symbol[s_list], only listed symbols exported.
blackList // -unexported_symbol[s_list], no listed symbol exported.
};
enum class DebugInfoMode {
addDebugMap, // Default
noDebugMap // -S option
};
/// Initializes the context to sane default values given the specified output
/// file type, arch, os, and minimum os version. This should be called before
/// other setXXX() methods.
void configure(HeaderFileType type, Arch arch, OS os, uint32_t minOSVersion);
void addPasses(PassManager &pm) override;
bool validateImpl(raw_ostream &diagnostics) override;
std::string demangle(StringRef symbolName) const override;
bool createImplicitFiles(std::vector<std::unique_ptr<File>> &) override;
uint32_t getCPUType() const;
uint32_t getCPUSubType() const;
bool addEntryPointLoadCommand() const;
bool addUnixThreadLoadCommand() const;
bool outputTypeHasEntry() const;
bool is64Bit() const;
virtual uint64_t pageZeroSize() const { return _pageZeroSize; }
virtual uint64_t pageSize() const { return _pageSize; }
mach_o::ArchHandler &archHandler() const;
HeaderFileType outputMachOType() const { return _outputMachOType; }
Arch arch() const { return _arch; }
StringRef archName() const { return nameFromArch(_arch); }
OS os() const { return _os; }
ExportMode exportMode() const { return _exportMode; }
void setExportMode(ExportMode mode) { _exportMode = mode; }
void addExportSymbol(StringRef sym);
bool exportRestrictMode() const { return _exportMode != ExportMode::globals; }
bool exportSymbolNamed(StringRef sym) const;
DebugInfoMode debugInfoMode() const { return _debugInfoMode; }
void setDebugInfoMode(DebugInfoMode mode) {
_debugInfoMode = mode;
}
void appendOrderedSymbol(StringRef symbol, StringRef filename);
bool keepPrivateExterns() const { return _keepPrivateExterns; }
void setKeepPrivateExterns(bool v) { _keepPrivateExterns = v; }
bool demangleSymbols() const { return _demangle; }
void setDemangleSymbols(bool d) { _demangle = d; }
/// Create file at specified path which will contain a binary encoding
/// of all input and output file paths.
std::error_code createDependencyFile(StringRef path);
void addInputFileDependency(StringRef path) const;
void addInputFileNotFound(StringRef path) const;
void addOutputFileDependency(StringRef path) const;
bool minOS(StringRef mac, StringRef iOS) const;
void setDoNothing(bool value) { _doNothing = value; }
bool doNothing() const { return _doNothing; }
bool printAtoms() const { return _printAtoms; }
bool testingFileUsage() const { return _testingFileUsage; }
const StringRefVector &searchDirs() const { return _searchDirs; }
const StringRefVector &frameworkDirs() const { return _frameworkDirs; }
void setSysLibRoots(const StringRefVector &paths);
const StringRefVector &sysLibRoots() const { return _syslibRoots; }
bool PIE() const { return _pie; }
void setPIE(bool pie) { _pie = pie; }
uint64_t baseAddress() const { return _baseAddress; }
void setBaseAddress(uint64_t baseAddress) { _baseAddress = baseAddress; }
/// \brief Checks whether a given path on the filesystem exists.
///
/// When running in -test_file_usage mode, this method consults an
/// internally maintained list of files that exist (provided by -path_exists)
/// instead of the actual filesystem.
bool pathExists(StringRef path) const;
/// Like pathExists() but only used on files - not directories.
bool fileExists(StringRef path) const;
/// \brief Adds any library search paths derived from the given base, possibly
/// modified by -syslibroots.
///
/// The set of paths added consists of approximately all syslibroot-prepended
/// versions of libPath that exist, or the original libPath if there are none
/// for whatever reason. With various edge-cases for compatibility.
void addModifiedSearchDir(StringRef libPath, bool isSystemPath = false);
/// \brief Determine whether -lFoo can be resolve within the given path, and
/// return the filename if so.
///
/// The -lFoo option is documented to search for libFoo.dylib and libFoo.a in
/// that order, unless Foo ends in ".o", in which case only the exact file
/// matches (e.g. -lfoo.o would only find foo.o).
ErrorOr<StringRef> searchDirForLibrary(StringRef path,
StringRef libName) const;
/// \brief Iterates through all search path entries looking for libName (as
/// specified by -lFoo).
ErrorOr<StringRef> searchLibrary(StringRef libName) const;
/// Add a framework search path. Internally, this method may be prepended
/// the path with syslibroot.
void addFrameworkSearchDir(StringRef fwPath, bool isSystemPath = false);
/// \brief Iterates through all framework directories looking for
/// Foo.framework/Foo (when fwName = "Foo").
ErrorOr<StringRef> findPathForFramework(StringRef fwName) const;
/// \brief The dylib's binary compatibility version, in the raw uint32 format.
///
/// When building a dynamic library, this is the compatibility version that
/// gets embedded into the result. Other Mach-O binaries that link against
/// this library will store the compatibility version in its load command. At
/// runtime, the loader will verify that the binary is compatible with the
/// installed dynamic library.
uint32_t compatibilityVersion() const { return _compatibilityVersion; }
/// \brief The dylib's current version, in the the raw uint32 format.
///
/// When building a dynamic library, this is the current version that gets
/// embedded into the result. Other Mach-O binaries that link against
/// this library will store the compatibility version in its load command.
uint32_t currentVersion() const { return _currentVersion; }
/// \brief The dylib's install name.
///
/// Binaries that link against the dylib will embed this path into the dylib
/// load command. When loading the binaries at runtime, this is the location
/// on disk that the loader will look for the dylib.
StringRef installName() const { return _installName; }
/// \brief Whether or not the dylib has side effects during initialization.
///
/// Dylibs marked as being dead strippable provide the guarantee that loading
/// the dylib has no side effects, allowing the linker to strip out the dylib
/// when linking a binary that does not use any of its symbols.
bool deadStrippableDylib() const { return _deadStrippableDylib; }
/// \brief The path to the executable that will load the bundle at runtime.
///
/// When building a Mach-O bundle, this executable will be examined if there
/// are undefined symbols after the main link phase. It is expected that this
/// binary will be loading the bundle at runtime and will provide the symbols
/// at that point.
StringRef bundleLoader() const { return _bundleLoader; }
void setCompatibilityVersion(uint32_t vers) { _compatibilityVersion = vers; }
void setCurrentVersion(uint32_t vers) { _currentVersion = vers; }
void setInstallName(StringRef name) { _installName = name; }
void setDeadStrippableDylib(bool deadStrippable) {
_deadStrippableDylib = deadStrippable;
}
void setBundleLoader(StringRef loader) { _bundleLoader = loader; }
void setPrintAtoms(bool value=true) { _printAtoms = value; }
void setTestingFileUsage(bool value = true) {
_testingFileUsage = value;
}
void addExistingPathForDebug(StringRef path) {
_existingPaths.insert(path);
}
void addRpath(StringRef rpath);
const StringRefVector &rpaths() const { return _rpaths; }
/// Add section alignment constraint on final layout.
void addSectionAlignment(StringRef seg, StringRef sect, uint8_t align2);
/// Returns true if specified section had alignment constraints.
bool sectionAligned(StringRef seg, StringRef sect, uint8_t &align2) const;
StringRef dyldPath() const { return "/usr/lib/dyld"; }
/// Stub creation Pass should be run.
bool needsStubsPass() const;
// GOT creation Pass should be run.
bool needsGOTPass() const;
/// Pass to transform __compact_unwind into __unwind_info should be run.
bool needsCompactUnwindPass() const;
/// Pass to add shims switching between thumb and arm mode.
bool needsShimPass() const;
/// Magic symbol name stubs will need to help lazy bind.
StringRef binderSymbolName() const;
/// Used to keep track of direct and indirect dylibs.
void registerDylib(mach_o::MachODylibFile *dylib, bool upward) const;
// Reads a file from disk to memory. Returns only a needed chunk
// if a fat binary.
ErrorOr<std::unique_ptr<MemoryBuffer>> getMemoryBuffer(StringRef path);
/// Used to find indirect dylibs. Instantiates a MachODylibFile if one
/// has not already been made for the requested dylib. Uses -L and -F
/// search paths to allow indirect dylibs to be overridden.
mach_o::MachODylibFile* findIndirectDylib(StringRef path);
uint32_t dylibCurrentVersion(StringRef installName) const;
uint32_t dylibCompatVersion(StringRef installName) const;
/// Creates a copy (owned by this MachOLinkingContext) of a string.
StringRef copy(StringRef str) { return str.copy(_allocator); }
/// If the memoryBuffer is a fat file with a slice for the current arch,
/// this method will return the offset and size of that slice.
bool sliceFromFatFile(const MemoryBuffer &mb, uint32_t &offset,
uint32_t &size);
/// Returns if a command line option specified dylib is an upward link.
bool isUpwardDylib(StringRef installName) const;
static bool isThinObjectFile(StringRef path, Arch &arch);
static Arch archFromCpuType(uint32_t cputype, uint32_t cpusubtype);
static Arch archFromName(StringRef archName);
static StringRef nameFromArch(Arch arch);
static uint32_t cpuTypeFromArch(Arch arch);
static uint32_t cpuSubtypeFromArch(Arch arch);
static bool is64Bit(Arch arch);
static bool isHostEndian(Arch arch);
static bool isBigEndian(Arch arch);
/// Construct 32-bit value from string "X.Y.Z" where
/// bits are xxxx.yy.zz. Largest number is 65535.255.255
static bool parsePackedVersion(StringRef str, uint32_t &result);
void finalizeInputFiles() override;
bool customAtomOrderer(const DefinedAtom *left, const DefinedAtom *right,
bool &leftBeforeRight) const;
private:
Writer &writer() const override;
mach_o::MachODylibFile* loadIndirectDylib(StringRef path);
void checkExportWhiteList(const DefinedAtom *atom) const;
void checkExportBlackList(const DefinedAtom *atom) const;
struct ArchInfo {
StringRef archName;
MachOLinkingContext::Arch arch;
bool littleEndian;
uint32_t cputype;
uint32_t cpusubtype;
};
struct SectionAlign {
StringRef segmentName;
StringRef sectionName;
uint8_t align2;
};
struct OrderFileNode {
StringRef fileFilter;
unsigned order;
};
static bool findOrderOrdinal(const std::vector<OrderFileNode> &nodes,
const DefinedAtom *atom, unsigned &ordinal);
static ArchInfo _s_archInfos[];
std::set<StringRef> _existingPaths; // For testing only.
StringRefVector _searchDirs;
StringRefVector _syslibRoots;
StringRefVector _frameworkDirs;
HeaderFileType _outputMachOType; // e.g MH_EXECUTE
bool _outputMachOTypeStatic; // Disambiguate static vs dynamic prog
bool _doNothing; // for -help and -v which just print info
bool _pie;
Arch _arch;
OS _os;
uint32_t _osMinVersion;
uint64_t _pageZeroSize;
uint64_t _pageSize;
uint64_t _baseAddress;
uint32_t _compatibilityVersion;
uint32_t _currentVersion;
StringRef _installName;
StringRefVector _rpaths;
bool _deadStrippableDylib;
bool _printAtoms;
bool _testingFileUsage;
bool _keepPrivateExterns;
bool _demangle;
StringRef _bundleLoader;
mutable std::unique_ptr<mach_o::ArchHandler> _archHandler;
mutable std::unique_ptr<Writer> _writer;
std::vector<SectionAlign> _sectAligns;
mutable llvm::StringMap<mach_o::MachODylibFile*> _pathToDylibMap;
mutable std::set<mach_o::MachODylibFile*> _allDylibs;
mutable std::set<mach_o::MachODylibFile*> _upwardDylibs;
mutable std::vector<std::unique_ptr<File>> _indirectDylibs;
ExportMode _exportMode;
llvm::StringSet<> _exportedSymbols;
DebugInfoMode _debugInfoMode;
std::unique_ptr<llvm::raw_fd_ostream> _dependencyInfo;
llvm::StringMap<std::vector<OrderFileNode>> _orderFiles;
unsigned _orderFileEntries;
};
} // end namespace lld
#endif

View file

@ -0,0 +1,463 @@
//===- lld/ReaderWriter/PECOFFLinkingContext.h ----------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_READER_WRITER_PECOFF_LINKING_CONTEXT_H
#define LLD_READER_WRITER_PECOFF_LINKING_CONTEXT_H
#include "lld/Core/LinkingContext.h"
#include "lld/Core/Reader.h"
#include "lld/Core/Writer.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/COFF.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileUtilities.h"
#include <map>
#include <mutex>
#include <set>
#include <vector>
using llvm::COFF::MachineTypes;
using llvm::COFF::WindowsSubsystem;
static const uint8_t DEFAULT_DOS_STUB[128] = {'M', 'Z'};
namespace lld {
class PECOFFLinkingContext : public LinkingContext {
public:
PECOFFLinkingContext()
: _mutex(), _allocMutex(), _hasEntry(true),
_baseAddress(invalidBaseAddress), _stackReserve(1024 * 1024),
_stackCommit(4096), _heapReserve(1024 * 1024), _heapCommit(4096),
_noDefaultLibAll(false), _sectionDefaultAlignment(4096),
_subsystem(llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN),
_machineType(llvm::COFF::IMAGE_FILE_MACHINE_I386), _imageVersion(0, 0),
_minOSVersion(6, 0), _nxCompat(true), _largeAddressAware(false),
_allowBind(true), _allowIsolation(true), _swapRunFromCD(false),
_swapRunFromNet(false), _baseRelocationEnabled(true),
_terminalServerAware(true), _dynamicBaseEnabled(true),
_createManifest(true), _embedManifest(false), _manifestId(1),
_manifestUAC(true), _manifestLevel("'asInvoker'"),
_manifestUiAccess("'false'"), _isDll(false), _highEntropyVA(true),
_requireSEH(false), _noSEH(false), _implib(""), _debug(false),
_pdbFilePath(""), _dosStub(llvm::makeArrayRef(DEFAULT_DOS_STUB)),
_parseDirectives(nullptr) {
setDeadStripping(true);
}
struct Version {
Version(int v1, int v2) : majorVersion(v1), minorVersion(v2) {}
int majorVersion;
int minorVersion;
};
struct ExportDesc {
ExportDesc()
: ordinal(-1), noname(false), isData(false), isPrivate(false) {}
bool operator<(const ExportDesc &other) const {
return getExternalName().compare(other.getExternalName()) < 0;
}
StringRef getRealName() const {
return mangledName.empty() ? name : mangledName;
}
StringRef getExternalName() const {
return externalName.empty() ? name : externalName;
}
std::string name;
std::string externalName;
std::string mangledName;
int ordinal;
bool noname;
bool isData;
bool isPrivate;
};
typedef bool (*ParseDirectives)(int, const char **, PECOFFLinkingContext &,
raw_ostream &);
/// \brief Casting support
static bool classof(const LinkingContext *info) { return true; }
Writer &writer() const override;
bool validateImpl(raw_ostream &diagnostics) override;
void addPasses(PassManager &pm) override;
bool createImplicitFiles(
std::vector<std::unique_ptr<File> > &result) override;
bool is64Bit() const {
return _machineType == llvm::COFF::IMAGE_FILE_MACHINE_AMD64;
}
// Returns a set of all defined symbols in input files.
const std::set<std::string> &definedSymbols();
/// Page size of x86 processor. Some data needs to be aligned at page boundary
/// when loaded into memory.
uint64_t getPageSize() const {
return 0x1000;
}
void appendInputSearchPath(StringRef dirPath) {
_inputSearchPaths.push_back(dirPath);
}
const std::vector<StringRef> getInputSearchPaths() {
return _inputSearchPaths;
}
void registerTemporaryFile(StringRef path) {
std::unique_ptr<llvm::FileRemover> fileRemover(
new llvm::FileRemover(Twine(allocate(path))));
_tempFiles.push_back(std::move(fileRemover));
}
StringRef searchLibraryFile(StringRef path) const;
StringRef decorateSymbol(StringRef name) const;
StringRef undecorateSymbol(StringRef name) const;
void setEntrySymbolName(StringRef name) { _entry = name; }
StringRef getEntrySymbolName() const { return _entry; }
void setHasEntry(bool val) { _hasEntry = val; }
bool hasEntry() const { return _hasEntry; }
void setBaseAddress(uint64_t addr) { _baseAddress = addr; }
uint64_t getBaseAddress() const;
void setStackReserve(uint64_t size) { _stackReserve = size; }
void setStackCommit(uint64_t size) { _stackCommit = size; }
uint64_t getStackReserve() const { return _stackReserve; }
uint64_t getStackCommit() const { return _stackCommit; }
void setHeapReserve(uint64_t size) { _heapReserve = size; }
void setHeapCommit(uint64_t size) { _heapCommit = size; }
uint64_t getHeapReserve() const { return _heapReserve; }
uint64_t getHeapCommit() const { return _heapCommit; }
void setSectionDefaultAlignment(uint32_t val) {
_sectionDefaultAlignment = val;
}
uint32_t getSectionDefaultAlignment() const {
return _sectionDefaultAlignment;
}
void setSubsystem(WindowsSubsystem ss) { _subsystem = ss; }
WindowsSubsystem getSubsystem() const { return _subsystem; }
void setMachineType(MachineTypes type) { _machineType = type; }
MachineTypes getMachineType() const { return _machineType; }
void setImageVersion(const Version &version) { _imageVersion = version; }
Version getImageVersion() const { return _imageVersion; }
void setMinOSVersion(const Version &version) { _minOSVersion = version; }
Version getMinOSVersion() const { return _minOSVersion; }
void setNxCompat(bool nxCompat) { _nxCompat = nxCompat; }
bool isNxCompat() const { return _nxCompat; }
void setLargeAddressAware(bool val) { _largeAddressAware = val; }
bool getLargeAddressAware() const { return _largeAddressAware; }
void setAllowBind(bool val) { _allowBind = val; }
bool getAllowBind() const { return _allowBind; }
void setAllowIsolation(bool val) { _allowIsolation = val; }
bool getAllowIsolation() const { return _allowIsolation; }
void setSwapRunFromCD(bool val) { _swapRunFromCD = val; }
bool getSwapRunFromCD() const { return _swapRunFromCD; }
void setSwapRunFromNet(bool val) { _swapRunFromNet = val; }
bool getSwapRunFromNet() const { return _swapRunFromNet; }
void setBaseRelocationEnabled(bool val) { _baseRelocationEnabled = val; }
bool getBaseRelocationEnabled() const { return _baseRelocationEnabled; }
void setTerminalServerAware(bool val) { _terminalServerAware = val; }
bool isTerminalServerAware() const { return _terminalServerAware; }
void setDynamicBaseEnabled(bool val) { _dynamicBaseEnabled = val; }
bool getDynamicBaseEnabled() const { return _dynamicBaseEnabled; }
void setCreateManifest(bool val) { _createManifest = val; }
bool getCreateManifest() const { return _createManifest; }
void setManifestOutputPath(std::string val) { _manifestOutputPath = val; }
const std::string &getManifestOutputPath() const {
return _manifestOutputPath;
}
void setEmbedManifest(bool val) { _embedManifest = val; }
bool getEmbedManifest() const { return _embedManifest; }
void setManifestId(int val) { _manifestId = val; }
int getManifestId() const { return _manifestId; }
void setManifestUAC(bool val) { _manifestUAC = val; }
bool getManifestUAC() const { return _manifestUAC; }
void setManifestLevel(std::string val) { _manifestLevel = std::move(val); }
const std::string &getManifestLevel() const { return _manifestLevel; }
void setManifestUiAccess(std::string val) { _manifestUiAccess = val; }
const std::string &getManifestUiAccess() const { return _manifestUiAccess; }
void setManifestDependency(std::string val) { _manifestDependency = val; }
const std::string &getManifestDependency() const {
return _manifestDependency;
}
void setIsDll(bool val) { _isDll = val; }
bool isDll() const { return _isDll; }
void setSafeSEH(bool val) {
if (val)
_requireSEH = true;
else
_noSEH = true;
}
bool requireSEH() const { return _requireSEH; }
bool noSEH() const { return _noSEH; }
void setHighEntropyVA(bool val) { _highEntropyVA = val; }
bool getHighEntropyVA() const { return _highEntropyVA; }
void setOutputImportLibraryPath(const std::string &val) { _implib = val; }
std::string getOutputImportLibraryPath() const;
void setDebug(bool val) { _debug = val; }
bool getDebug() { return _debug; }
void setPDBFilePath(StringRef str) { _pdbFilePath = str; }
std::string getPDBFilePath() const;
void addDelayLoadDLL(StringRef dll) {
_delayLoadDLLs.insert(dll.lower());
}
bool isDelayLoadDLL(StringRef dll) const {
return _delayLoadDLLs.count(dll.lower()) == 1;
}
StringRef getOutputSectionName(StringRef sectionName) const;
bool addSectionRenaming(raw_ostream &diagnostics,
StringRef from, StringRef to);
const std::set<std::string> &getAlternateNames(StringRef name) {
return _alternateNames[name];
}
void addAlternateName(StringRef weak, StringRef def) {
_alternateNames[def].insert(weak);
}
void addNoDefaultLib(StringRef path) {
if (path.endswith_lower(".lib"))
_noDefaultLibs.insert(path.drop_back(4).lower());
else
_noDefaultLibs.insert(path.lower());
}
bool hasNoDefaultLib(StringRef path) const {
if (path.endswith_lower(".lib"))
return _noDefaultLibs.count(path.drop_back(4).lower()) > 0;
return _noDefaultLibs.count(path.lower()) > 0;
}
void setNoDefaultLibAll(bool val) { _noDefaultLibAll = val; }
bool getNoDefaultLibAll() const { return _noDefaultLibAll; }
void setSectionSetMask(StringRef sectionName, uint32_t flags);
void setSectionClearMask(StringRef sectionName, uint32_t flags);
uint32_t getSectionAttributes(StringRef sectionName, uint32_t flags) const;
void setDosStub(ArrayRef<uint8_t> data) { _dosStub = data; }
ArrayRef<uint8_t> getDosStub() const { return _dosStub; }
void addDllExport(ExportDesc &desc);
std::vector<ExportDesc> &getDllExports() { return _dllExports; }
const std::vector<ExportDesc> &getDllExports() const { return _dllExports; }
StringRef getDelayLoadHelperName() const {
return is64Bit() ? "__delayLoadHelper2" : "___delayLoadHelper2@8";
}
StringRef allocate(StringRef ref) const {
_allocMutex.lock();
char *x = _allocator.Allocate<char>(ref.size() + 1);
_allocMutex.unlock();
memcpy(x, ref.data(), ref.size());
x[ref.size()] = '\0';
return x;
}
ArrayRef<uint8_t> allocate(ArrayRef<uint8_t> array) const {
size_t size = array.size();
_allocMutex.lock();
uint8_t *p = _allocator.Allocate<uint8_t>(size);
_allocMutex.unlock();
memcpy(p, array.data(), size);
return ArrayRef<uint8_t>(p, p + array.size());
}
template <typename T> T &allocateCopy(const T &x) const {
_allocMutex.lock();
T *r = new (_allocator) T(x);
_allocMutex.unlock();
return *r;
}
void addLibraryFile(std::unique_ptr<FileNode> file);
void setModuleDefinitionFile(const std::string val) {
_moduleDefinitionFile = val;
}
std::string getModuleDefinitionFile() const {
return _moduleDefinitionFile;
}
std::recursive_mutex &getMutex() { return _mutex; }
void setParseDirectives(ParseDirectives parseDirectives) {
_parseDirectives = parseDirectives;
}
ParseDirectives getParseDirectives() {
return _parseDirectives;
}
protected:
/// Method to create a internal file for the entry symbol
std::unique_ptr<File> createEntrySymbolFile() const override;
/// Method to create a internal file for an undefined symbol
std::unique_ptr<File> createUndefinedSymbolFile() const override;
private:
enum : uint64_t {
invalidBaseAddress = UINT64_MAX,
pe32DefaultBaseAddress = 0x400000U,
pe32PlusDefaultBaseAddress = 0x140000000U
};
std::recursive_mutex _mutex;
mutable std::mutex _allocMutex;
std::string _entry;
// False if /noentry option is given.
bool _hasEntry;
// The start address for the program. The default value for the executable is
// 0x400000, but can be altered using /base command line option.
uint64_t _baseAddress;
uint64_t _stackReserve;
uint64_t _stackCommit;
uint64_t _heapReserve;
uint64_t _heapCommit;
bool _noDefaultLibAll;
uint32_t _sectionDefaultAlignment;
WindowsSubsystem _subsystem;
MachineTypes _machineType;
Version _imageVersion;
Version _minOSVersion;
bool _nxCompat;
bool _largeAddressAware;
bool _allowBind;
bool _allowIsolation;
bool _swapRunFromCD;
bool _swapRunFromNet;
bool _baseRelocationEnabled;
bool _terminalServerAware;
bool _dynamicBaseEnabled;
bool _createManifest;
std::string _manifestOutputPath;
bool _embedManifest;
int _manifestId;
bool _manifestUAC;
std::string _manifestLevel;
std::string _manifestUiAccess;
std::string _manifestDependency;
bool _isDll;
bool _highEntropyVA;
// True if /SAFESEH option is specified. Valid only for x86. If true, LLD will
// produce an image with SEH table. If any modules were not compatible with
// SEH, LLD will exit with an error.
bool _requireSEH;
// True if /SAFESEH:no option is specified. Valid only for x86. If true, LLD
// will not produce an image with SEH table even if all input object files are
// compatible with SEH.
bool _noSEH;
// /IMPLIB command line option.
std::string _implib;
// True if /DEBUG is given.
bool _debug;
// PDB file output path. NB: this is dummy -- LLD just creates the empty file.
std::string _pdbFilePath;
// /DELAYLOAD option.
std::set<std::string> _delayLoadDLLs;
// The set to store /nodefaultlib arguments.
std::set<std::string> _noDefaultLibs;
std::vector<StringRef> _inputSearchPaths;
std::unique_ptr<Writer> _writer;
// A map for weak aliases.
std::map<std::string, std::set<std::string>> _alternateNames;
// A map for section renaming. For example, if there is an entry in the map
// whose value is .rdata -> .text, the section contens of .rdata will be
// merged to .text in the resulting executable.
std::map<std::string, std::string> _renamedSections;
// Section attributes specified by /section option.
std::map<std::string, uint32_t> _sectionSetMask;
std::map<std::string, uint32_t> _sectionClearMask;
// DLLExport'ed symbols.
std::vector<ExportDesc> _dllExports;
// List of files that will be removed on destruction.
std::vector<std::unique_ptr<llvm::FileRemover> > _tempFiles;
// DOS Stub. DOS stub is data located at the beginning of PE/COFF file.
// Windows loader do not really care about DOS stub contents, but it's usually
// a small DOS program that prints out a message "This program requires
// Microsoft Windows." This feature was somewhat useful before Windows 95.
ArrayRef<uint8_t> _dosStub;
// Name of the temporary file for lib.exe subcommand. For debugging
// only.
std::string _moduleDefinitionFile;
std::set<std::string> _definedSyms;
std::set<Node *> _seen;
ParseDirectives _parseDirectives;
};
} // end namespace lld
#endif

View file

@ -0,0 +1,57 @@
//===- lld/ReaderWriter/RelocationHelperFunctions.h------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_READER_WRITER_RELOCATION_HELPER_FUNCTIONS_H
#define LLD_READER_WRITER_RELOCATION_HELPER_FUNCTIONS_H
namespace lld {
/// Gather val's bits as specified by the mask. Example:
///
/// Val: 0bABCDEFGHIJKLMN
/// Mask: 0b10111100001011
/// Output: 0b000000ACDEFKMN
template <typename T> T gatherBits(T val, T mask) {
T result = 0;
size_t off = 0;
for (size_t bit = 0; bit < sizeof(T) * 8; ++bit) {
bool maskBit = (mask >> bit) & 1;
if (maskBit) {
bool valBit = (val >> bit) & 1;
result |= static_cast<T>(valBit) << off;
++off;
}
}
return result;
}
/// Scatter val's bits as specified by the mask. Example:
///
/// Val: 0bABCDEFG
/// Mask: 0b10111100001011
/// Output: 0b00ABCD0000E0FG
template <typename T> T scatterBits(T val, T mask) {
T result = 0;
size_t off = 0;
for (size_t bit = 0; bit < sizeof(T) * 8; ++bit) {
bool maskBit = (mask >> bit) & 1;
if (maskBit) {
bool valBit = (val >> off) & 1;
result |= static_cast<T>(valBit) << bit;
++off;
}
}
return result;
}
} // namespace lld
#endif // LLD_READER_WRITER_RELOCATION_HELPER_FUNCTIONS_H

View file

@ -0,0 +1,46 @@
//===- lld/ReaderWriter/YamlContext.h - object used in YAML I/O context ---===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_READER_WRITER_YAML_CONTEXT_H
#define LLD_READER_WRITER_YAML_CONTEXT_H
#include "lld/Core/LLVM.h"
#include <functional>
#include <memory>
#include <vector>
namespace lld {
class File;
class LinkingContext;
namespace mach_o {
namespace normalized {
struct NormalizedFile;
}
}
using lld::mach_o::normalized::NormalizedFile;
/// When YAML I/O is used in lld, the yaml context always holds a YamlContext
/// object. We need to support hetergenous yaml documents which each require
/// different context info. This struct supports all clients.
struct YamlContext {
YamlContext()
: _linkingContext(nullptr), _registry(nullptr), _file(nullptr),
_normalizeMachOFile(nullptr) {}
const LinkingContext *_linkingContext;
const Registry *_registry;
File *_file;
NormalizedFile *_normalizeMachOFile;
StringRef _path;
};
} // end namespace lld
#endif // LLD_READER_WRITER_YAML_CONTEXT_H

4
lib/CMakeLists.txt Normal file
View file

@ -0,0 +1,4 @@
add_subdirectory(Config)
add_subdirectory(Core)
add_subdirectory(Driver)
add_subdirectory(ReaderWriter)

View file

@ -0,0 +1,5 @@
add_llvm_library(lldConfig
Version.cpp
LINK_LIBS
LLVMSupport
)

13
lib/Config/Makefile Normal file
View file

@ -0,0 +1,13 @@
##===- lib/Config/Makefile ---------------------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
LLD_LEVEL := ../..
LIBRARYNAME := lldConfig
include $(LLD_LEVEL)/Makefile

66
lib/Config/Version.cpp Normal file
View file

@ -0,0 +1,66 @@
//===- lib/Config/Version.cpp - LLD Version Number ---------------*- C++-=====//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines several version-related utility functions for LLD.
//
//===----------------------------------------------------------------------===//
#include "lld/Config/Version.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdlib>
#include <cstring>
using namespace llvm;
namespace lld {
StringRef getLLDRepositoryPath() {
#ifdef LLD_REPOSITORY_STRING
return LLD_REPOSITORY_STRING;
#else
return "";
#endif
}
StringRef getLLDRevision() {
#ifdef LLD_REVISION_STRING
return LLD_REVISION_STRING;
#else
return "";
#endif
}
std::string getLLDRepositoryVersion() {
std::string buf;
llvm::raw_string_ostream OS(buf);
std::string Path = getLLDRepositoryPath();
std::string Revision = getLLDRevision();
if (!Path.empty() || !Revision.empty()) {
OS << '(';
if (!Path.empty())
OS << Path;
if (!Revision.empty()) {
if (!Path.empty())
OS << ' ';
OS << Revision;
}
OS << ')';
}
return OS.str();
}
StringRef getLLDVersion() {
#ifdef LLD_VERSION_STRING
return LLD_VERSION_STRING;
#else
return "";
#endif
}
} // end namespace lld

12
lib/Core/CMakeLists.txt Normal file
View file

@ -0,0 +1,12 @@
add_llvm_library(lldCore
DefinedAtom.cpp
Error.cpp
File.cpp
LinkingContext.cpp
Reader.cpp
Resolver.cpp
SymbolTable.cpp
Writer.cpp
LINK_LIBS
LLVMSupport
)

96
lib/Core/DefinedAtom.cpp Normal file
View file

@ -0,0 +1,96 @@
//===- DefinedAtom.cpp ------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/ErrorHandling.h"
#include "lld/Core/DefinedAtom.h"
#include "lld/Core/File.h"
namespace lld {
DefinedAtom::ContentPermissions DefinedAtom::permissions() const {
// By default base permissions on content type.
return permissions(this->contentType());
}
// Utility function for deriving permissions from content type
DefinedAtom::ContentPermissions DefinedAtom::permissions(ContentType type) {
switch (type) {
case typeCode:
case typeResolver:
case typeBranchIsland:
case typeBranchShim:
case typeStub:
case typeStubHelper:
case typeMachHeader:
return permR_X;
case typeConstant:
case typeCString:
case typeUTF16String:
case typeCFI:
case typeLSDA:
case typeLiteral4:
case typeLiteral8:
case typeLiteral16:
case typeDTraceDOF:
case typeCompactUnwindInfo:
case typeProcessedUnwindInfo:
case typeRONote:
case typeNoAlloc:
return permR__;
case typeData:
case typeDataFast:
case typeZeroFill:
case typeZeroFillFast:
case typeObjC1Class:
case typeLazyPointer:
case typeLazyDylibPointer:
case typeThunkTLV:
case typeRWNote:
return permRW_;
case typeGOT:
case typeConstData:
case typeCFString:
case typeInitializerPtr:
case typeTerminatorPtr:
case typeCStringPtr:
case typeObjCClassPtr:
case typeObjC2CategoryList:
case typeInterposingTuples:
case typeTLVInitialData:
case typeTLVInitialZeroFill:
case typeTLVInitializerPtr:
case typeThreadData:
case typeThreadZeroFill:
return permRW_L;
case typeGroupComdat:
case typeGnuLinkOnce:
case typeUnknown:
case typeTempLTO:
return permUnknown;
}
llvm_unreachable("unknown content type");
}
bool DefinedAtom::compareByPosition(const DefinedAtom *lhs,
const DefinedAtom *rhs) {
if (lhs == rhs)
return false;
const File *lhsFile = &lhs->file();
const File *rhsFile = &rhs->file();
if (lhsFile->ordinal() != rhsFile->ordinal())
return lhsFile->ordinal() < rhsFile->ordinal();
assert(lhs->ordinal() != rhs->ordinal());
return lhs->ordinal() < rhs->ordinal();
}
} // namespace

151
lib/Core/Error.cpp Normal file
View file

@ -0,0 +1,151 @@
//===- Error.cpp - system_error extensions for lld --------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lld/Core/Error.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/ErrorHandling.h"
#include <mutex>
#include <string>
#include <vector>
using namespace lld;
class _NativeReaderErrorCategory : public std::error_category {
public:
const char* name() const LLVM_NOEXCEPT override {
return "lld.native.reader";
}
std::string message(int ev) const override {
switch (static_cast<NativeReaderError>(ev)) {
case NativeReaderError::success:
return "Success";
case NativeReaderError::unknown_file_format:
return "Unknown file format";
case NativeReaderError::file_too_short:
return "file truncated";
case NativeReaderError::file_malformed:
return "file malformed";
case NativeReaderError::memory_error:
return "out of memory";
case NativeReaderError::unknown_chunk_type:
return "unknown chunk type";
case NativeReaderError::conflicting_target_machine:
return "conflicting target machine";
}
llvm_unreachable("An enumerator of NativeReaderError does not have a "
"message defined.");
}
};
const std::error_category &lld::native_reader_category() {
static _NativeReaderErrorCategory o;
return o;
}
class _YamlReaderErrorCategory : public std::error_category {
public:
const char* name() const LLVM_NOEXCEPT override {
return "lld.yaml.reader";
}
std::string message(int ev) const override {
switch (static_cast<YamlReaderError>(ev)) {
case YamlReaderError::success:
return "Success";
case YamlReaderError::unknown_keyword:
return "Unknown keyword found in yaml file";
case YamlReaderError::illegal_value:
return "Bad value found in yaml file";
}
llvm_unreachable("An enumerator of YamlReaderError does not have a "
"message defined.");
}
};
const std::error_category &lld::YamlReaderCategory() {
static _YamlReaderErrorCategory o;
return o;
}
class _LinkerScriptReaderErrorCategory : public std::error_category {
public:
const char *name() const LLVM_NOEXCEPT override {
return "lld.linker-script.reader";
}
std::string message(int ev) const override {
switch (static_cast<LinkerScriptReaderError>(ev)) {
case LinkerScriptReaderError::success:
return "Success";
case LinkerScriptReaderError::parse_error:
return "Error parsing linker script";
case LinkerScriptReaderError::unknown_symbol_in_expr:
return "Unknown symbol found when evaluating linker script expression";
case LinkerScriptReaderError::unrecognized_function_in_expr:
return "Unrecognized function call when evaluating linker script "
"expression";
}
llvm_unreachable("An enumerator of LinkerScriptReaderError does not have a "
"message defined.");
}
};
const std::error_category &lld::LinkerScriptReaderCategory() {
static _LinkerScriptReaderErrorCategory o;
return o;
}
namespace lld {
/// Temporary class to enable make_dynamic_error_code() until
/// llvm::ErrorOr<> is updated to work with error encapsulations
/// other than error_code.
class dynamic_error_category : public std::error_category {
public:
~dynamic_error_category() LLVM_NOEXCEPT {}
const char *name() const LLVM_NOEXCEPT override {
return "lld.dynamic_error";
}
std::string message(int ev) const override {
assert(ev >= 0);
assert(ev < (int)_messages.size());
// The value is an index into the string vector.
return _messages[ev];
}
int add(std::string msg) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
// Value zero is always the successs value.
if (_messages.empty())
_messages.push_back("Success");
_messages.push_back(msg);
// Return the index of the string just appended.
return _messages.size() - 1;
}
private:
std::vector<std::string> _messages;
std::recursive_mutex _mutex;
};
static dynamic_error_category categorySingleton;
std::error_code make_dynamic_error_code(StringRef msg) {
return std::error_code(categorySingleton.add(msg), categorySingleton);
}
std::error_code make_dynamic_error_code(const Twine &msg) {
return std::error_code(categorySingleton.add(msg.str()), categorySingleton);
}
}

30
lib/Core/File.cpp Normal file
View file

@ -0,0 +1,30 @@
//===- Core/File.cpp - A Container of Atoms -------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lld/Core/File.h"
#include "lld/Core/LLVM.h"
#include <mutex>
namespace lld {
File::~File() {}
File::atom_collection_empty<DefinedAtom> File::_noDefinedAtoms;
File::atom_collection_empty<UndefinedAtom> File::_noUndefinedAtoms;
File::atom_collection_empty<SharedLibraryAtom> File::_noSharedLibraryAtoms;
File::atom_collection_empty<AbsoluteAtom> File::_noAbsoluteAtoms;
std::error_code File::parse() {
std::lock_guard<std::mutex> lock(_parseMutex);
if (!_lastError.hasValue())
_lastError = doParse();
return _lastError.getValue();
}
} // namespace lld

104
lib/Core/LinkingContext.cpp Normal file
View file

@ -0,0 +1,104 @@
//===- lib/Core/LinkingContext.cpp - Linker Context Object Interface ------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lld/Core/Alias.h"
#include "lld/Core/LinkingContext.h"
#include "lld/Core/Resolver.h"
#include "lld/Core/Simple.h"
#include "lld/Core/Writer.h"
#include "llvm/ADT/Triple.h"
namespace lld {
LinkingContext::LinkingContext()
: _deadStrip(false), _allowDuplicates(false),
_globalsAreDeadStripRoots(false),
_searchArchivesToOverrideTentativeDefinitions(false),
_searchSharedLibrariesToOverrideTentativeDefinitions(false),
_warnIfCoalesableAtomsHaveDifferentCanBeNull(false),
_warnIfCoalesableAtomsHaveDifferentLoadName(false),
_printRemainingUndefines(true), _allowRemainingUndefines(false),
_logInputFiles(false), _allowShlibUndefines(false),
_outputFileType(OutputFileType::Default), _nextOrdinal(0) {}
LinkingContext::~LinkingContext() {}
bool LinkingContext::validate(raw_ostream &diagnostics) {
return validateImpl(diagnostics);
}
std::error_code LinkingContext::writeFile(const File &linkedFile) const {
return this->writer().writeFile(linkedFile, _outputPath);
}
bool LinkingContext::createImplicitFiles(
std::vector<std::unique_ptr<File> > &result) {
return this->writer().createImplicitFiles(result);
}
std::unique_ptr<File> LinkingContext::createEntrySymbolFile() const {
return createEntrySymbolFile("<command line option -e>");
}
std::unique_ptr<File>
LinkingContext::createEntrySymbolFile(StringRef filename) const {
if (entrySymbolName().empty())
return nullptr;
std::unique_ptr<SimpleFile> entryFile(new SimpleFile(filename));
entryFile->addAtom(
*(new (_allocator) SimpleUndefinedAtom(*entryFile, entrySymbolName())));
return std::move(entryFile);
}
std::unique_ptr<File> LinkingContext::createUndefinedSymbolFile() const {
return createUndefinedSymbolFile("<command line option -u or --defsym>");
}
std::unique_ptr<File>
LinkingContext::createUndefinedSymbolFile(StringRef filename) const {
if (_initialUndefinedSymbols.empty())
return nullptr;
std::unique_ptr<SimpleFile> undefinedSymFile(new SimpleFile(filename));
for (StringRef undefSym : _initialUndefinedSymbols)
undefinedSymFile->addAtom(*(new (_allocator) SimpleUndefinedAtom(
*undefinedSymFile, undefSym)));
return std::move(undefinedSymFile);
}
std::unique_ptr<File> LinkingContext::createAliasSymbolFile() const {
if (getAliases().empty())
return nullptr;
std::unique_ptr<SimpleFile> file(new SimpleFile("<alias>"));
for (const auto &i : getAliases()) {
StringRef from = i.first;
StringRef to = i.second;
SimpleDefinedAtom *fromAtom = new (_allocator) AliasAtom(*file, from);
UndefinedAtom *toAtom = new (_allocator) SimpleUndefinedAtom(*file, to);
fromAtom->addReference(Reference::KindNamespace::all,
Reference::KindArch::all, Reference::kindLayoutAfter,
0, toAtom, 0);
file->addAtom(*fromAtom);
file->addAtom(*toAtom);
}
return std::move(file);
}
void LinkingContext::createInternalFiles(
std::vector<std::unique_ptr<File> > &result) const {
if (std::unique_ptr<File> file = createEntrySymbolFile())
result.push_back(std::move(file));
if (std::unique_ptr<File> file = createUndefinedSymbolFile())
result.push_back(std::move(file));
if (std::unique_ptr<File> file = createAliasSymbolFile())
result.push_back(std::move(file));
}
void LinkingContext::addPasses(PassManager &pm) {}
} // end namespace lld

13
lib/Core/Makefile Normal file
View file

@ -0,0 +1,13 @@
##===- lld/lib/Core/Makefile ---------------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
LLD_LEVEL := ../..
LIBRARYNAME := lldCore
include $(LLD_LEVEL)/Makefile

117
lib/Core/Reader.cpp Normal file
View file

@ -0,0 +1,117 @@
//===- lib/Core/Reader.cpp ------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lld/Core/File.h"
#include "lld/Core/Reader.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include <memory>
#include <system_error>
namespace lld {
YamlIOTaggedDocumentHandler::~YamlIOTaggedDocumentHandler() {}
void Registry::add(std::unique_ptr<Reader> reader) {
_readers.push_back(std::move(reader));
}
void Registry::add(std::unique_ptr<YamlIOTaggedDocumentHandler> handler) {
_yamlHandlers.push_back(std::move(handler));
}
std::error_code
Registry::loadFile(std::unique_ptr<MemoryBuffer> mb,
std::vector<std::unique_ptr<File>> &result) const {
// Get file type.
StringRef content(mb->getBufferStart(), mb->getBufferSize());
llvm::sys::fs::file_magic fileType = llvm::sys::fs::identify_magic(content);
// Get file extension.
StringRef extension = llvm::sys::path::extension(mb->getBufferIdentifier());
// Ask each registered reader if it can handle this file type or extension.
for (const std::unique_ptr<Reader> &reader : _readers) {
if (!reader->canParse(fileType, extension, *mb))
continue;
if (std::error_code ec = reader->loadFile(std::move(mb), *this, result))
return ec;
return std::error_code();
}
// No Reader could parse this file.
return make_error_code(llvm::errc::executable_format_error);
}
static const Registry::KindStrings kindStrings[] = {
{Reference::kindLayoutAfter, "layout-after"},
{Reference::kindGroupChild, "group-child"},
{Reference::kindAssociate, "associate"},
LLD_KIND_STRING_END};
Registry::Registry() {
addKindTable(Reference::KindNamespace::all, Reference::KindArch::all,
kindStrings);
}
bool Registry::handleTaggedDoc(llvm::yaml::IO &io,
const lld::File *&file) const {
for (const std::unique_ptr<YamlIOTaggedDocumentHandler> &h : _yamlHandlers)
if (h->handledDocTag(io, file))
return true;
return false;
}
void Registry::addKindTable(Reference::KindNamespace ns,
Reference::KindArch arch,
const KindStrings array[]) {
KindEntry entry = { ns, arch, array };
_kindEntries.push_back(entry);
}
bool Registry::referenceKindFromString(StringRef inputStr,
Reference::KindNamespace &ns,
Reference::KindArch &arch,
Reference::KindValue &value) const {
for (const KindEntry &entry : _kindEntries) {
for (const KindStrings *pair = entry.array; !pair->name.empty(); ++pair) {
if (!inputStr.equals(pair->name))
continue;
ns = entry.ns;
arch = entry.arch;
value = pair->value;
return true;
}
}
return false;
}
bool Registry::referenceKindToString(Reference::KindNamespace ns,
Reference::KindArch arch,
Reference::KindValue value,
StringRef &str) const {
for (const KindEntry &entry : _kindEntries) {
if (entry.ns != ns)
continue;
if (entry.arch != arch)
continue;
for (const KindStrings *pair = entry.array; !pair->name.empty(); ++pair) {
if (pair->value != value)
continue;
str = pair->name;
return true;
}
}
return false;
}
} // end namespace lld

516
lib/Core/Resolver.cpp Normal file
View file

@ -0,0 +1,516 @@
//===- Core/Resolver.cpp - Resolves Atom References -----------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lld/Core/Atom.h"
#include "lld/Core/ArchiveLibraryFile.h"
#include "lld/Core/File.h"
#include "lld/Core/Instrumentation.h"
#include "lld/Core/LLVM.h"
#include "lld/Core/LinkingContext.h"
#include "lld/Core/Resolver.h"
#include "lld/Core/SharedLibraryFile.h"
#include "lld/Core/SymbolTable.h"
#include "lld/Core/UndefinedAtom.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
#include <utility>
#include <vector>
namespace lld {
bool Resolver::handleFile(File &file) {
bool undefAdded = false;
for (const DefinedAtom *atom : file.defined())
doDefinedAtom(*atom);
for (const UndefinedAtom *atom : file.undefined()) {
if (doUndefinedAtom(*atom)) {
undefAdded = true;
maybePreloadArchiveMember(atom->name());
}
}
for (const SharedLibraryAtom *atom : file.sharedLibrary())
doSharedLibraryAtom(*atom);
for (const AbsoluteAtom *atom : file.absolute())
doAbsoluteAtom(*atom);
return undefAdded;
}
void Resolver::forEachUndefines(File &file, bool searchForOverrides,
UndefCallback callback) {
size_t i = _undefineIndex[&file];
do {
for (; i < _undefines.size(); ++i) {
StringRef undefName = _undefines[i];
if (undefName.empty())
continue;
const Atom *atom = _symbolTable.findByName(undefName);
if (!isa<UndefinedAtom>(atom) || _symbolTable.isCoalescedAway(atom)) {
// The symbol was resolved by some other file. Cache the result.
_undefines[i] = "";
continue;
}
callback(undefName, false);
}
if (!searchForOverrides)
continue;
for (StringRef tentDefName : _symbolTable.tentativeDefinitions()) {
// Load for previous tentative may also have loaded
// something that overrode this tentative, so always check.
const Atom *curAtom = _symbolTable.findByName(tentDefName);
assert(curAtom != nullptr);
if (const DefinedAtom *curDefAtom = dyn_cast<DefinedAtom>(curAtom))
if (curDefAtom->merge() == DefinedAtom::mergeAsTentative)
callback(tentDefName, true);
}
} while (i < _undefines.size());
_undefineIndex[&file] = i;
}
bool Resolver::handleArchiveFile(File &file) {
ArchiveLibraryFile *archiveFile = cast<ArchiveLibraryFile>(&file);
bool searchForOverrides =
_ctx.searchArchivesToOverrideTentativeDefinitions();
bool undefAdded = false;
forEachUndefines(file, searchForOverrides,
[&](StringRef undefName, bool dataSymbolOnly) {
if (File *member = archiveFile->find(undefName, dataSymbolOnly)) {
member->setOrdinal(_ctx.getNextOrdinalAndIncrement());
member->beforeLink();
updatePreloadArchiveMap();
undefAdded = handleFile(*member) || undefAdded;
}
});
return undefAdded;
}
void Resolver::handleSharedLibrary(File &file) {
// Add all the atoms from the shared library
SharedLibraryFile *sharedLibrary = cast<SharedLibraryFile>(&file);
handleFile(*sharedLibrary);
bool searchForOverrides =
_ctx.searchSharedLibrariesToOverrideTentativeDefinitions();
forEachUndefines(file, searchForOverrides,
[&](StringRef undefName, bool dataSymbolOnly) {
if (const SharedLibraryAtom *atom =
sharedLibrary->exports(undefName, dataSymbolOnly))
doSharedLibraryAtom(*atom);
});
}
bool Resolver::doUndefinedAtom(const UndefinedAtom &atom) {
DEBUG_WITH_TYPE("resolver", llvm::dbgs()
<< " UndefinedAtom: "
<< llvm::format("0x%09lX", &atom)
<< ", name=" << atom.name() << "\n");
// add to list of known atoms
_atoms.push_back(&atom);
// tell symbol table
bool newUndefAdded = _symbolTable.add(atom);
if (newUndefAdded)
_undefines.push_back(atom.name());
// If the undefined symbol has an alternative name, try to resolve the
// symbol with the name to give it a second chance. This feature is used
// for COFF "weak external" symbol.
if (newUndefAdded || !_symbolTable.isDefined(atom.name())) {
if (const UndefinedAtom *fallbackAtom = atom.fallback()) {
doUndefinedAtom(*fallbackAtom);
_symbolTable.addReplacement(&atom, fallbackAtom);
}
}
return newUndefAdded;
}
/// \brief Add the section group and the group-child reference members.
void Resolver::maybeAddSectionGroupOrGnuLinkOnce(const DefinedAtom &atom) {
// First time adding a group?
bool isFirstTime = _symbolTable.addGroup(atom);
if (!isFirstTime) {
// If duplicate symbols are allowed, select the first group.
if (_ctx.getAllowDuplicates())
return;
auto *prevGroup = dyn_cast<DefinedAtom>(_symbolTable.findGroup(atom.name()));
assert(prevGroup &&
"Internal Error: The group atom could only be a defined atom");
// The atoms should be of the same content type, reject invalid group
// resolution behaviors.
if (atom.contentType() == prevGroup->contentType())
return;
llvm::errs() << "SymbolTable: error while merging " << atom.name()
<< "\n";
llvm::report_fatal_error("duplicate symbol error");
return;
}
for (const Reference *r : atom) {
if (r->kindNamespace() == lld::Reference::KindNamespace::all &&
r->kindValue() == lld::Reference::kindGroupChild) {
const DefinedAtom *target = dyn_cast<DefinedAtom>(r->target());
assert(target && "Internal Error: kindGroupChild references need to "
"be associated with Defined Atoms only");
_atoms.push_back(target);
_symbolTable.add(*target);
}
}
}
// Called on each atom when a file is added. Returns true if a given
// atom is added to the symbol table.
void Resolver::doDefinedAtom(const DefinedAtom &atom) {
DEBUG_WITH_TYPE("resolver", llvm::dbgs()
<< " DefinedAtom: "
<< llvm::format("0x%09lX", &atom)
<< ", file=#"
<< atom.file().ordinal()
<< ", atom=#"
<< atom.ordinal()
<< ", name="
<< atom.name()
<< "\n");
// add to list of known atoms
_atoms.push_back(&atom);
if (atom.isGroupParent()) {
maybeAddSectionGroupOrGnuLinkOnce(atom);
} else {
_symbolTable.add(atom);
}
// An atom that should never be dead-stripped is a dead-strip root.
if (_ctx.deadStrip() && atom.deadStrip() == DefinedAtom::deadStripNever) {
_deadStripRoots.insert(&atom);
}
}
void Resolver::doSharedLibraryAtom(const SharedLibraryAtom &atom) {
DEBUG_WITH_TYPE("resolver", llvm::dbgs()
<< " SharedLibraryAtom: "
<< llvm::format("0x%09lX", &atom)
<< ", name="
<< atom.name()
<< "\n");
// add to list of known atoms
_atoms.push_back(&atom);
// tell symbol table
_symbolTable.add(atom);
}
void Resolver::doAbsoluteAtom(const AbsoluteAtom &atom) {
DEBUG_WITH_TYPE("resolver", llvm::dbgs()
<< " AbsoluteAtom: "
<< llvm::format("0x%09lX", &atom)
<< ", name="
<< atom.name()
<< "\n");
// add to list of known atoms
_atoms.push_back(&atom);
// tell symbol table
if (atom.scope() != Atom::scopeTranslationUnit)
_symbolTable.add(atom);
}
// utility to add a vector of atoms
void Resolver::addAtoms(const std::vector<const DefinedAtom *> &newAtoms) {
for (const DefinedAtom *newAtom : newAtoms)
doDefinedAtom(*newAtom);
}
// Instantiate an archive file member if there's a file containing a
// defined symbol for a given symbol name. Instantiation is done in a
// different worker thread and has no visible side effect.
void Resolver::maybePreloadArchiveMember(StringRef sym) {
auto it = _archiveMap.find(sym);
if (it == _archiveMap.end())
return;
ArchiveLibraryFile *archive = it->second;
archive->preload(_ctx.getTaskGroup(), sym);
}
// Returns true if at least one of N previous files has created an
// undefined symbol.
bool Resolver::undefinesAdded(int begin, int end) {
std::vector<std::unique_ptr<Node>> &inputs = _ctx.getNodes();
for (int i = begin; i < end; ++i)
if (FileNode *node = dyn_cast<FileNode>(inputs[i].get()))
if (_newUndefinesAdded[node->getFile()])
return true;
return false;
}
File *Resolver::getFile(int &index) {
std::vector<std::unique_ptr<Node>> &inputs = _ctx.getNodes();
if ((size_t)index >= inputs.size())
return nullptr;
if (GroupEnd *group = dyn_cast<GroupEnd>(inputs[index].get())) {
// We are at the end of the current group. If one or more new
// undefined atom has been added in the last groupSize files, we
// reiterate over the files.
int size = group->getSize();
if (undefinesAdded(index - size, index)) {
index -= size;
return getFile(index);
}
++index;
return getFile(index);
}
return cast<FileNode>(inputs[index++].get())->getFile();
}
// Update a map of Symbol -> ArchiveFile. The map is used for speculative
// file loading.
void Resolver::updatePreloadArchiveMap() {
std::vector<std::unique_ptr<Node>> &nodes = _ctx.getNodes();
for (int i = nodes.size() - 1; i >= 0; --i) {
auto *fnode = dyn_cast<FileNode>(nodes[i].get());
if (!fnode)
continue;
auto *archive = dyn_cast<ArchiveLibraryFile>(fnode->getFile());
if (!archive || _archiveSeen.count(archive))
continue;
_archiveSeen.insert(archive);
for (StringRef sym : archive->getDefinedSymbols())
_archiveMap[sym] = archive;
}
}
// Keep adding atoms until _ctx.getNextFile() returns an error. This
// function is where undefined atoms are resolved.
bool Resolver::resolveUndefines() {
ScopedTask task(getDefaultDomain(), "resolveUndefines");
int index = 0;
std::set<File *> seen;
for (;;) {
bool undefAdded = false;
File *file = getFile(index);
if (!file)
return true;
if (std::error_code ec = file->parse()) {
llvm::errs() << "Cannot open " + file->path()
<< ": " << ec.message() << "\n";
return false;
}
file->beforeLink();
updatePreloadArchiveMap();
switch (file->kind()) {
case File::kindObject:
// The same file may be visited more than once if the file is
// in --start-group and --end-group. Only library files should
// be processed more than once.
if (seen.count(file))
break;
seen.insert(file);
assert(!file->hasOrdinal());
file->setOrdinal(_ctx.getNextOrdinalAndIncrement());
undefAdded = handleFile(*file);
break;
case File::kindArchiveLibrary:
if (!file->hasOrdinal())
file->setOrdinal(_ctx.getNextOrdinalAndIncrement());
undefAdded = handleArchiveFile(*file);
break;
case File::kindSharedLibrary:
if (!file->hasOrdinal())
file->setOrdinal(_ctx.getNextOrdinalAndIncrement());
handleSharedLibrary(*file);
break;
}
_newUndefinesAdded[file] = undefAdded;
}
}
// switch all references to undefined or coalesced away atoms
// to the new defined atom
void Resolver::updateReferences() {
ScopedTask task(getDefaultDomain(), "updateReferences");
for (const Atom *atom : _atoms) {
if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom)) {
for (const Reference *ref : *defAtom) {
// A reference of type kindAssociate should't be updated.
// Instead, an atom having such reference will be removed
// if the target atom is coalesced away, so that they will
// go away as a group.
if (ref->kindNamespace() == lld::Reference::KindNamespace::all &&
ref->kindValue() == lld::Reference::kindAssociate) {
if (_symbolTable.isCoalescedAway(atom))
_deadAtoms.insert(ref->target());
continue;
}
const Atom *newTarget = _symbolTable.replacement(ref->target());
const_cast<Reference *>(ref)->setTarget(newTarget);
}
}
}
}
// For dead code stripping, recursively mark atoms "live"
void Resolver::markLive(const Atom *atom) {
// Mark the atom is live. If it's already marked live, then stop recursion.
auto exists = _liveAtoms.insert(atom);
if (!exists.second)
return;
// Mark all atoms it references as live
if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom)) {
for (const Reference *ref : *defAtom)
markLive(ref->target());
for (auto &p : llvm::make_range(_reverseRef.equal_range(defAtom))) {
const Atom *target = p.second;
markLive(target);
}
}
}
static bool isBackref(const Reference *ref) {
if (ref->kindNamespace() != lld::Reference::KindNamespace::all)
return false;
return (ref->kindValue() == lld::Reference::kindLayoutAfter ||
ref->kindValue() == lld::Reference::kindGroupChild);
}
// remove all atoms not actually used
void Resolver::deadStripOptimize() {
ScopedTask task(getDefaultDomain(), "deadStripOptimize");
// only do this optimization with -dead_strip
if (!_ctx.deadStrip())
return;
// Some type of references prevent referring atoms to be dead-striped.
// Make a reverse map of such references before traversing the graph.
// While traversing the list of atoms, mark AbsoluteAtoms as live
// in order to avoid reclaim.
for (const Atom *atom : _atoms) {
if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom))
for (const Reference *ref : *defAtom)
if (isBackref(ref))
_reverseRef.insert(std::make_pair(ref->target(), atom));
if (const AbsoluteAtom *absAtom = dyn_cast<AbsoluteAtom>(atom))
markLive(absAtom);
}
// By default, shared libraries are built with all globals as dead strip roots
if (_ctx.globalsAreDeadStripRoots())
for (const Atom *atom : _atoms)
if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom))
if (defAtom->scope() == DefinedAtom::scopeGlobal)
_deadStripRoots.insert(defAtom);
// Or, use list of names that are dead strip roots.
for (const StringRef &name : _ctx.deadStripRoots()) {
const Atom *symAtom = _symbolTable.findByName(name);
assert(symAtom);
_deadStripRoots.insert(symAtom);
}
// mark all roots as live, and recursively all atoms they reference
for (const Atom *dsrAtom : _deadStripRoots)
markLive(dsrAtom);
// now remove all non-live atoms from _atoms
_atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), [&](const Atom *a) {
return _liveAtoms.count(a) == 0;
}),
_atoms.end());
}
// error out if some undefines remain
bool Resolver::checkUndefines() {
// build vector of remaining undefined symbols
std::vector<const UndefinedAtom *> undefinedAtoms = _symbolTable.undefines();
if (_ctx.deadStrip()) {
// When dead code stripping, we don't care if dead atoms are undefined.
undefinedAtoms.erase(
std::remove_if(undefinedAtoms.begin(), undefinedAtoms.end(),
[&](const Atom *a) { return _liveAtoms.count(a) == 0; }),
undefinedAtoms.end());
}
if (undefinedAtoms.empty())
return false;
// Warn about unresolved symbols.
bool foundUndefines = false;
for (const UndefinedAtom *undef : undefinedAtoms) {
// Skip over a weak symbol.
if (undef->canBeNull() != UndefinedAtom::canBeNullNever)
continue;
// If this is a library and undefined symbols are allowed on the
// target platform, skip over it.
if (isa<SharedLibraryFile>(undef->file()) && _ctx.allowShlibUndefines())
continue;
// If the undefine is coalesced away, skip over it.
if (_symbolTable.isCoalescedAway(undef))
continue;
// Seems like this symbol is undefined. Warn that.
foundUndefines = true;
if (_ctx.printRemainingUndefines()) {
llvm::errs() << "Undefined symbol: " << undef->file().path()
<< ": " << _ctx.demangle(undef->name())
<< "\n";
}
}
if (!foundUndefines)
return false;
if (_ctx.printRemainingUndefines())
llvm::errs() << "symbol(s) not found\n";
return true;
}
// remove from _atoms all coaleseced away atoms
void Resolver::removeCoalescedAwayAtoms() {
ScopedTask task(getDefaultDomain(), "removeCoalescedAwayAtoms");
_atoms.erase(std::remove_if(_atoms.begin(), _atoms.end(), [&](const Atom *a) {
return _symbolTable.isCoalescedAway(a) || _deadAtoms.count(a);
}),
_atoms.end());
}
bool Resolver::resolve() {
updatePreloadArchiveMap();
if (!resolveUndefines())
return false;
updateReferences();
deadStripOptimize();
if (checkUndefines())
if (!_ctx.allowRemainingUndefines())
return false;
removeCoalescedAwayAtoms();
_result->addAtoms(_atoms);
return true;
}
void Resolver::MergedFile::addAtoms(std::vector<const Atom *> &all) {
ScopedTask task(getDefaultDomain(), "addAtoms");
DEBUG_WITH_TYPE("resolver", llvm::dbgs() << "Resolver final atom list:\n");
for (const Atom *atom : all) {
DEBUG_WITH_TYPE("resolver", llvm::dbgs()
<< llvm::format(" 0x%09lX", atom)
<< ", name="
<< atom->name()
<< "\n");
addAtom(*atom);
}
}
} // namespace lld

390
lib/Core/SymbolTable.cpp Normal file
View file

@ -0,0 +1,390 @@
//===- Core/SymbolTable.cpp - Main Symbol Table ---------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lld/Core/SymbolTable.h"
#include "lld/Core/AbsoluteAtom.h"
#include "lld/Core/Atom.h"
#include "lld/Core/DefinedAtom.h"
#include "lld/Core/File.h"
#include "lld/Core/LLVM.h"
#include "lld/Core/LinkingContext.h"
#include "lld/Core/Resolver.h"
#include "lld/Core/SharedLibraryAtom.h"
#include "lld/Core/UndefinedAtom.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <vector>
namespace lld {
SymbolTable::SymbolTable(LinkingContext &context) : _context(context) {}
bool SymbolTable::add(const UndefinedAtom &atom) { return addByName(atom); }
bool SymbolTable::add(const SharedLibraryAtom &atom) { return addByName(atom); }
bool SymbolTable::add(const AbsoluteAtom &atom) { return addByName(atom); }
bool SymbolTable::add(const DefinedAtom &atom) {
if (!atom.name().empty() &&
atom.scope() != DefinedAtom::scopeTranslationUnit) {
// Named atoms cannot be merged by content.
assert(atom.merge() != DefinedAtom::mergeByContent);
// Track named atoms that are not scoped to file (static).
return addByName(atom);
}
if (atom.merge() == DefinedAtom::mergeByContent) {
// Named atoms cannot be merged by content.
assert(atom.name().empty());
// Currently only read-only constants can be merged.
if (atom.permissions() == DefinedAtom::permR__)
return addByContent(atom);
// TODO: support mergeByContent of data atoms by comparing content & fixups.
}
return false;
}
const Atom *SymbolTable::findGroup(StringRef sym) {
NameToAtom::iterator pos = _groupTable.find(sym);
if (pos == _groupTable.end())
return nullptr;
return pos->second;
}
bool SymbolTable::addGroup(const DefinedAtom &da) {
StringRef name = da.name();
assert(!name.empty());
const Atom *existing = findGroup(name);
if (existing == nullptr) {
_groupTable[name] = &da;
return true;
}
_replacedAtoms[&da] = existing;
return false;
}
enum NameCollisionResolution {
NCR_First,
NCR_Second,
NCR_DupDef,
NCR_DupUndef,
NCR_DupShLib,
NCR_Error
};
static NameCollisionResolution cases[4][4] = {
//regular absolute undef sharedLib
{
// first is regular
NCR_DupDef, NCR_Error, NCR_First, NCR_First
},
{
// first is absolute
NCR_Error, NCR_Error, NCR_First, NCR_First
},
{
// first is undef
NCR_Second, NCR_Second, NCR_DupUndef, NCR_Second
},
{
// first is sharedLib
NCR_Second, NCR_Second, NCR_First, NCR_DupShLib
}
};
static NameCollisionResolution collide(Atom::Definition first,
Atom::Definition second) {
return cases[first][second];
}
enum MergeResolution {
MCR_First,
MCR_Second,
MCR_Largest,
MCR_SameSize,
MCR_Error
};
static MergeResolution mergeCases[][6] = {
// no tentative weak weakAddress sameNameAndSize largest
{MCR_Error, MCR_First, MCR_First, MCR_First, MCR_SameSize, MCR_Largest}, // no
{MCR_Second, MCR_Largest, MCR_Second, MCR_Second, MCR_SameSize, MCR_Largest}, // tentative
{MCR_Second, MCR_First, MCR_First, MCR_Second, MCR_SameSize, MCR_Largest}, // weak
{MCR_Second, MCR_First, MCR_First, MCR_First, MCR_SameSize, MCR_Largest}, // weakAddress
{MCR_SameSize, MCR_SameSize, MCR_SameSize, MCR_SameSize, MCR_SameSize, MCR_SameSize}, // sameSize
{MCR_Largest, MCR_Largest, MCR_Largest, MCR_Largest, MCR_SameSize, MCR_Largest}, // largest
};
static MergeResolution mergeSelect(DefinedAtom::Merge first,
DefinedAtom::Merge second) {
assert(first != DefinedAtom::mergeByContent);
assert(second != DefinedAtom::mergeByContent);
return mergeCases[first][second];
}
bool SymbolTable::addByName(const Atom &newAtom) {
StringRef name = newAtom.name();
assert(!name.empty());
const Atom *existing = findByName(name);
if (existing == nullptr) {
// Name is not in symbol table yet, add it associate with this atom.
_nameTable[name] = &newAtom;
return true;
}
// Do nothing if the same object is added more than once.
if (existing == &newAtom)
return false;
// Name is already in symbol table and associated with another atom.
bool useNew = true;
switch (collide(existing->definition(), newAtom.definition())) {
case NCR_First:
useNew = false;
break;
case NCR_Second:
useNew = true;
break;
case NCR_DupDef: {
const auto *existingDef = cast<DefinedAtom>(existing);
const auto *newDef = cast<DefinedAtom>(&newAtom);
switch (mergeSelect(existingDef->merge(), newDef->merge())) {
case MCR_First:
useNew = false;
break;
case MCR_Second:
useNew = true;
break;
case MCR_Largest: {
uint64_t existingSize = existingDef->sectionSize();
uint64_t newSize = newDef->sectionSize();
useNew = (newSize >= existingSize);
break;
}
case MCR_SameSize: {
uint64_t existingSize = existingDef->sectionSize();
uint64_t newSize = newDef->sectionSize();
if (existingSize == newSize) {
useNew = true;
break;
}
llvm::errs() << "Size mismatch: "
<< existing->name() << " (" << existingSize << ") "
<< newAtom.name() << " (" << newSize << ")\n";
// fallthrough
}
case MCR_Error:
if (!_context.getAllowDuplicates()) {
llvm::errs() << "Duplicate symbols: "
<< existing->name()
<< ":"
<< existing->file().path()
<< " and "
<< newAtom.name()
<< ":"
<< newAtom.file().path()
<< "\n";
llvm::report_fatal_error("duplicate symbol error");
}
useNew = false;
break;
}
break;
}
case NCR_DupUndef: {
const UndefinedAtom* existingUndef = cast<UndefinedAtom>(existing);
const UndefinedAtom* newUndef = cast<UndefinedAtom>(&newAtom);
bool sameCanBeNull = (existingUndef->canBeNull() == newUndef->canBeNull());
if (!sameCanBeNull &&
_context.warnIfCoalesableAtomsHaveDifferentCanBeNull()) {
llvm::errs() << "lld warning: undefined symbol "
<< existingUndef->name()
<< " has different weakness in "
<< existingUndef->file().path()
<< " and in " << newUndef->file().path() << "\n";
}
const UndefinedAtom *existingFallback = existingUndef->fallback();
const UndefinedAtom *newFallback = newUndef->fallback();
bool hasDifferentFallback =
(existingFallback && newFallback &&
existingFallback->name() != newFallback->name());
if (hasDifferentFallback) {
llvm::errs() << "lld warning: undefined symbol "
<< existingUndef->name() << " has different fallback: "
<< existingFallback->name() << " in "
<< existingUndef->file().path() << " and "
<< newFallback->name() << " in "
<< newUndef->file().path() << "\n";
}
bool hasNewFallback = newUndef->fallback();
if (sameCanBeNull)
useNew = hasNewFallback;
else
useNew = (newUndef->canBeNull() < existingUndef->canBeNull());
break;
}
case NCR_DupShLib: {
const SharedLibraryAtom *curShLib = cast<SharedLibraryAtom>(existing);
const SharedLibraryAtom *newShLib = cast<SharedLibraryAtom>(&newAtom);
bool sameNullness =
(curShLib->canBeNullAtRuntime() == newShLib->canBeNullAtRuntime());
bool sameName = curShLib->loadName().equals(newShLib->loadName());
if (sameName && !sameNullness &&
_context.warnIfCoalesableAtomsHaveDifferentCanBeNull()) {
// FIXME: need diagonstics interface for writing warning messages
llvm::errs() << "lld warning: shared library symbol "
<< curShLib->name() << " has different weakness in "
<< curShLib->file().path() << " and in "
<< newShLib->file().path();
}
if (!sameName && _context.warnIfCoalesableAtomsHaveDifferentLoadName()) {
// FIXME: need diagonstics interface for writing warning messages
llvm::errs() << "lld warning: shared library symbol "
<< curShLib->name() << " has different load path in "
<< curShLib->file().path() << " and in "
<< newShLib->file().path();
}
useNew = false;
break;
}
case NCR_Error:
llvm::errs() << "SymbolTable: error while merging " << name << "\n";
llvm::report_fatal_error("duplicate symbol error");
break;
}
// Give context a chance to change which is kept.
_context.notifySymbolTableCoalesce(existing, &newAtom, useNew);
if (useNew) {
// Update name table to use new atom.
_nameTable[name] = &newAtom;
// Add existing atom to replacement table.
_replacedAtoms[existing] = &newAtom;
} else {
// New atom is not being used. Add it to replacement table.
_replacedAtoms[&newAtom] = existing;
}
return false;
}
unsigned SymbolTable::AtomMappingInfo::getHashValue(const DefinedAtom *atom) {
auto content = atom->rawContent();
return llvm::hash_combine(atom->size(),
atom->contentType(),
llvm::hash_combine_range(content.begin(),
content.end()));
}
bool SymbolTable::AtomMappingInfo::isEqual(const DefinedAtom * const l,
const DefinedAtom * const r) {
if (l == r)
return true;
if (l == getEmptyKey())
return false;
if (r == getEmptyKey())
return false;
if (l == getTombstoneKey())
return false;
if (r == getTombstoneKey())
return false;
if (l->contentType() != r->contentType())
return false;
if (l->size() != r->size())
return false;
if (l->sectionChoice() != r->sectionChoice())
return false;
if (l->sectionChoice() == DefinedAtom::sectionCustomRequired) {
if (!l->customSectionName().equals(r->customSectionName()))
return false;
}
ArrayRef<uint8_t> lc = l->rawContent();
ArrayRef<uint8_t> rc = r->rawContent();
return memcmp(lc.data(), rc.data(), lc.size()) == 0;
}
bool SymbolTable::addByContent(const DefinedAtom &newAtom) {
AtomContentSet::iterator pos = _contentTable.find(&newAtom);
if (pos == _contentTable.end()) {
_contentTable.insert(&newAtom);
return true;
}
const Atom* existing = *pos;
// New atom is not being used. Add it to replacement table.
_replacedAtoms[&newAtom] = existing;
return false;
}
const Atom *SymbolTable::findByName(StringRef sym) {
NameToAtom::iterator pos = _nameTable.find(sym);
if (pos == _nameTable.end())
return nullptr;
return pos->second;
}
bool SymbolTable::isDefined(StringRef sym) {
if (const Atom *atom = findByName(sym))
return !isa<UndefinedAtom>(atom);
return false;
}
void SymbolTable::addReplacement(const Atom *replaced,
const Atom *replacement) {
_replacedAtoms[replaced] = replacement;
}
const Atom *SymbolTable::replacement(const Atom *atom) {
// Find the replacement for a given atom. Atoms in _replacedAtoms
// may be chained, so find the last one.
for (;;) {
AtomToAtom::iterator pos = _replacedAtoms.find(atom);
if (pos == _replacedAtoms.end())
return atom;
atom = pos->second;
}
}
bool SymbolTable::isCoalescedAway(const Atom *atom) {
return _replacedAtoms.count(atom) > 0;
}
std::vector<const UndefinedAtom *> SymbolTable::undefines() {
std::vector<const UndefinedAtom *> ret;
for (auto it : _nameTable) {
const Atom *atom = it.second;
assert(atom != nullptr);
if (const auto *undef = dyn_cast<const UndefinedAtom>(atom))
if (_replacedAtoms.count(undef) == 0)
ret.push_back(undef);
}
return ret;
}
std::vector<StringRef> SymbolTable::tentativeDefinitions() {
std::vector<StringRef> ret;
for (auto entry : _nameTable) {
const Atom *atom = entry.second;
StringRef name = entry.first;
assert(atom != nullptr);
if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom))
if (defAtom->merge() == DefinedAtom::mergeAsTentative)
ret.push_back(name);
}
return ret;
}
} // namespace lld

18
lib/Core/TODO.txt Normal file
View file

@ -0,0 +1,18 @@
lib/Core
~~~~~~~~
* Add endianness support to the native reader and writer.
* The NativeReader has lots of similar code for converting arrays of ivar
data in mapped memory into arrays of objects. The commonality can be
factored out, maybe templatized.
* The NativeFileFormat.h is old school C structs and constants. We scope
things better by defining constants used with a struct inside the struct
declaration.
* The native reader and writer currently just blast in memory enumeration
values (e.g. DefinedAtom::Scope) into a byte in the disk format. To support
future changes to the enumerations, there should be a translation layer
to map disk values to in-memory values.

23
lib/Core/Writer.cpp Normal file
View file

@ -0,0 +1,23 @@
//===- lib/Core/Writer.cpp ------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lld/Core/File.h"
#include "lld/Core/Writer.h"
namespace lld {
Writer::Writer() {
}
Writer::~Writer() {
}
bool Writer::createImplicitFiles(std::vector<std::unique_ptr<File> > &) {
return true;
}
} // end namespace lld

43
lib/Driver/CMakeLists.txt Normal file
View file

@ -0,0 +1,43 @@
set(LLVM_TARGET_DEFINITIONS UniversalDriverOptions.td)
tablegen(LLVM UniversalDriverOptions.inc -gen-opt-parser-defs)
set(LLVM_TARGET_DEFINITIONS GnuLdOptions.td)
tablegen(LLVM GnuLdOptions.inc -gen-opt-parser-defs)
set(LLVM_TARGET_DEFINITIONS CoreOptions.td)
tablegen(LLVM CoreOptions.inc -gen-opt-parser-defs)
set(LLVM_TARGET_DEFINITIONS DarwinLdOptions.td)
tablegen(LLVM DarwinLdOptions.inc -gen-opt-parser-defs)
set(LLVM_TARGET_DEFINITIONS WinLinkOptions.td)
tablegen(LLVM WinLinkOptions.inc -gen-opt-parser-defs)
add_public_tablegen_target(DriverOptionsTableGen)
add_llvm_library(lldDriver
CoreDriver.cpp
DarwinLdDriver.cpp
Driver.cpp
GnuLdDriver.cpp
UniversalDriver.cpp
WinLinkDriver.cpp
WinLinkModuleDef.cpp
LINK_LIBS
lldConfig
lldMachO
lldPECOFF
lldELF
lldAArch64ELFTarget
lldARMELFTarget
lldHexagonELFTarget
lldMipsELFTarget
lldX86ELFTarget
lldExampleSubTarget
lldX86_64ELFTarget
lldCore
lldNative
lldReaderWriter
lldYAML
LLVMObject
LLVMOption
LLVMSupport
)
add_dependencies(lldDriver DriverOptionsTableGen)

172
lib/Driver/CoreDriver.cpp Normal file
View file

@ -0,0 +1,172 @@
//===- lib/Driver/CoreDriver.cpp ------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lld/Core/Reader.h"
#include "lld/Driver/Driver.h"
#include "lld/ReaderWriter/CoreLinkingContext.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
using namespace lld;
namespace {
// Create enum with OPT_xxx values for each option in CoreOptions.td
enum {
OPT_INVALID = 0,
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELP, META) \
OPT_##ID,
#include "CoreOptions.inc"
#undef OPTION
};
// Create prefix string literals used in CoreOptions.td
#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
#include "CoreOptions.inc"
#undef PREFIX
// Create table mapping all options defined in CoreOptions.td
static const llvm::opt::OptTable::Info infoTable[] = {
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR) \
{ PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \
PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS },
#include "CoreOptions.inc"
#undef OPTION
};
// Create OptTable class for parsing actual command line arguments
class CoreOptTable : public llvm::opt::OptTable {
public:
CoreOptTable() : OptTable(infoTable, llvm::array_lengthof(infoTable)){}
};
} // namespace anonymous
namespace lld {
static const Registry::KindStrings coreKindStrings[] = {
{ CoreLinkingContext::TEST_RELOC_CALL32, "call32" },
{ CoreLinkingContext::TEST_RELOC_PCREL32, "pcrel32" },
{ CoreLinkingContext::TEST_RELOC_GOT_LOAD32, "gotLoad32" },
{ CoreLinkingContext::TEST_RELOC_GOT_USE32, "gotUse32" },
{ CoreLinkingContext::TEST_RELOC_LEA32_WAS_GOT, "lea32wasGot" },
LLD_KIND_STRING_END
};
bool CoreDriver::link(int argc, const char *argv[], raw_ostream &diagnostics) {
CoreLinkingContext ctx;
// Register possible input file parsers.
ctx.registry().addSupportNativeObjects();
ctx.registry().addSupportYamlFiles();
ctx.registry().addKindTable(Reference::KindNamespace::testing,
Reference::KindArch::all, coreKindStrings);
if (!parse(argc, argv, ctx))
return false;
return Driver::link(ctx);
}
bool CoreDriver::parse(int argc, const char *argv[], CoreLinkingContext &ctx,
raw_ostream &diagnostics) {
// Parse command line options using CoreOptions.td
std::unique_ptr<llvm::opt::InputArgList> parsedArgs;
CoreOptTable table;
unsigned missingIndex;
unsigned missingCount;
parsedArgs.reset(
table.ParseArgs(&argv[1], &argv[argc], missingIndex, missingCount));
if (missingCount) {
diagnostics << "error: missing arg value for '"
<< parsedArgs->getArgString(missingIndex) << "' expected "
<< missingCount << " argument(s).\n";
return false;
}
// Set default options
ctx.setOutputPath("-");
ctx.setDeadStripping(false);
ctx.setGlobalsAreDeadStripRoots(false);
ctx.setPrintRemainingUndefines(false);
ctx.setAllowRemainingUndefines(true);
ctx.setSearchArchivesToOverrideTentativeDefinitions(false);
// Process all the arguments and create input files.
for (auto inputArg : *parsedArgs) {
switch (inputArg->getOption().getID()) {
case OPT_mllvm:
ctx.appendLLVMOption(inputArg->getValue());
break;
case OPT_entry:
ctx.setEntrySymbolName(inputArg->getValue());
break;
case OPT_output:
ctx.setOutputPath(inputArg->getValue());
break;
case OPT_dead_strip:
ctx.setDeadStripping(true);
break;
case OPT_keep_globals:
ctx.setGlobalsAreDeadStripRoots(true);
break;
case OPT_undefines_are_errors:
ctx.setPrintRemainingUndefines(true);
ctx.setAllowRemainingUndefines(false);
break;
case OPT_commons_search_archives:
ctx.setSearchArchivesToOverrideTentativeDefinitions(true);
break;
case OPT_add_pass:
ctx.addPassNamed(inputArg->getValue());
break;
case OPT_INPUT: {
std::vector<std::unique_ptr<File>> files
= loadFile(ctx, inputArg->getValue(), false);
for (std::unique_ptr<File> &file : files)
ctx.getNodes().push_back(llvm::make_unique<FileNode>(std::move(file)));
break;
}
default:
break;
}
}
if (ctx.getNodes().empty()) {
diagnostics << "No input files\n";
return false;
}
// Validate the combination of options used.
return ctx.validate(diagnostics);
}
} // namespace lld

15
lib/Driver/CoreOptions.td Normal file
View file

@ -0,0 +1,15 @@
include "llvm/Option/OptParser.td"
def output : Separate<["-"], "o">;
def entry : Separate<["-"], "e">;
def dead_strip : Flag<["--"], "dead-strip">;
def undefines_are_errors : Flag<["--"], "undefines-are-errors">;
def keep_globals : Flag<["--"], "keep-globals">;
def commons_search_archives : Flag<["--"], "commons-search-archives">;
def add_pass : Separate<["--"], "add-pass">;
def target : Separate<["-"], "target">, HelpText<"Target triple to link for">;
def mllvm : Separate<["-"], "mllvm">, HelpText<"Options to pass to LLVM">;

View file

@ -0,0 +1,832 @@
//===- lib/Driver/DarwinLdDriver.cpp --------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// Concrete instance of the Driver for darwin's ld.
///
//===----------------------------------------------------------------------===//
#include "lld/Core/File.h"
#include "lld/Core/ArchiveLibraryFile.h"
#include "lld/Core/SharedLibraryFile.h"
#include "lld/Driver/Driver.h"
#include "lld/ReaderWriter/MachOLinkingContext.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/MachO.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
using namespace lld;
namespace {
// Create enum with OPT_xxx values for each option in DarwinLdOptions.td
enum {
OPT_INVALID = 0,
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELP, META) \
OPT_##ID,
#include "DarwinLdOptions.inc"
#undef OPTION
};
// Create prefix string literals used in DarwinLdOptions.td
#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
#include "DarwinLdOptions.inc"
#undef PREFIX
// Create table mapping all options defined in DarwinLdOptions.td
static const llvm::opt::OptTable::Info infoTable[] = {
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR) \
{ PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \
PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS },
#include "DarwinLdOptions.inc"
#undef OPTION
};
// Create OptTable class for parsing actual command line arguments
class DarwinLdOptTable : public llvm::opt::OptTable {
public:
DarwinLdOptTable() : OptTable(infoTable, llvm::array_lengthof(infoTable)){}
};
std::vector<std::unique_ptr<File>>
loadFile(MachOLinkingContext &ctx, StringRef path,
raw_ostream &diag, bool wholeArchive, bool upwardDylib) {
if (ctx.logInputFiles())
diag << path << "\n";
ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr = ctx.getMemoryBuffer(path);
if (std::error_code ec = mbOrErr.getError())
return makeErrorFile(path, ec);
std::vector<std::unique_ptr<File>> files;
if (std::error_code ec = ctx.registry().loadFile(std::move(mbOrErr.get()), files))
return makeErrorFile(path, ec);
for (std::unique_ptr<File> &pf : files) {
// If file is a dylib, inform LinkingContext about it.
if (SharedLibraryFile *shl = dyn_cast<SharedLibraryFile>(pf.get())) {
if (std::error_code ec = shl->parse())
return makeErrorFile(path, ec);
ctx.registerDylib(reinterpret_cast<mach_o::MachODylibFile*>(shl),
upwardDylib);
}
}
if (wholeArchive)
return parseMemberFiles(files);
return files;
}
} // anonymous namespace
// Test may be running on Windows. Canonicalize the path
// separator to '/' to get consistent outputs for tests.
static std::string canonicalizePath(StringRef path) {
char sep = llvm::sys::path::get_separator().front();
if (sep != '/') {
std::string fixedPath = path;
std::replace(fixedPath.begin(), fixedPath.end(), sep, '/');
return fixedPath;
} else {
return path;
}
}
static void addFile(StringRef path, MachOLinkingContext &ctx,
bool loadWholeArchive,
bool upwardDylib, raw_ostream &diag) {
std::vector<std::unique_ptr<File>> files =
loadFile(ctx, path, diag, loadWholeArchive, upwardDylib);
for (std::unique_ptr<File> &file : files)
ctx.getNodes().push_back(llvm::make_unique<FileNode>(std::move(file)));
}
// Export lists are one symbol per line. Blank lines are ignored.
// Trailing comments start with #.
static std::error_code parseExportsList(StringRef exportFilePath,
MachOLinkingContext &ctx,
raw_ostream &diagnostics) {
// Map in export list file.
ErrorOr<std::unique_ptr<MemoryBuffer>> mb =
MemoryBuffer::getFileOrSTDIN(exportFilePath);
if (std::error_code ec = mb.getError())
return ec;
ctx.addInputFileDependency(exportFilePath);
StringRef buffer = mb->get()->getBuffer();
while (!buffer.empty()) {
// Split off each line in the file.
std::pair<StringRef, StringRef> lineAndRest = buffer.split('\n');
StringRef line = lineAndRest.first;
// Ignore trailing # comments.
std::pair<StringRef, StringRef> symAndComment = line.split('#');
StringRef sym = symAndComment.first.trim();
if (!sym.empty())
ctx.addExportSymbol(sym);
buffer = lineAndRest.second;
}
return std::error_code();
}
/// Order files are one symbol per line. Blank lines are ignored.
/// Trailing comments start with #. Symbol names can be prefixed with an
/// architecture name and/or .o leaf name. Examples:
/// _foo
/// bar.o:_bar
/// libfrob.a(bar.o):_bar
/// x86_64:_foo64
static std::error_code parseOrderFile(StringRef orderFilePath,
MachOLinkingContext &ctx,
raw_ostream &diagnostics) {
// Map in order file.
ErrorOr<std::unique_ptr<MemoryBuffer>> mb =
MemoryBuffer::getFileOrSTDIN(orderFilePath);
if (std::error_code ec = mb.getError())
return ec;
ctx.addInputFileDependency(orderFilePath);
StringRef buffer = mb->get()->getBuffer();
while (!buffer.empty()) {
// Split off each line in the file.
std::pair<StringRef, StringRef> lineAndRest = buffer.split('\n');
StringRef line = lineAndRest.first;
buffer = lineAndRest.second;
// Ignore trailing # comments.
std::pair<StringRef, StringRef> symAndComment = line.split('#');
if (symAndComment.first.empty())
continue;
StringRef sym = symAndComment.first.trim();
if (sym.empty())
continue;
// Check for prefix.
StringRef prefix;
std::pair<StringRef, StringRef> prefixAndSym = sym.split(':');
if (!prefixAndSym.second.empty()) {
sym = prefixAndSym.second;
prefix = prefixAndSym.first;
if (!prefix.endswith(".o") && !prefix.endswith(".o)")) {
// If arch name prefix does not match arch being linked, ignore symbol.
if (!ctx.archName().equals(prefix))
continue;
prefix = "";
}
} else
sym = prefixAndSym.first;
if (!sym.empty()) {
ctx.appendOrderedSymbol(sym, prefix);
//llvm::errs() << sym << ", prefix=" << prefix << "\n";
}
}
return std::error_code();
}
//
// There are two variants of the -filelist option:
//
// -filelist <path>
// In this variant, the path is to a text file which contains one file path
// per line. There are no comments or trimming of whitespace.
//
// -fileList <path>,<dir>
// In this variant, the path is to a text file which contains a partial path
// per line. The <dir> prefix is prepended to each partial path.
//
static std::error_code loadFileList(StringRef fileListPath,
MachOLinkingContext &ctx, bool forceLoad,
raw_ostream &diagnostics) {
// If there is a comma, split off <dir>.
std::pair<StringRef, StringRef> opt = fileListPath.split(',');
StringRef filePath = opt.first;
StringRef dirName = opt.second;
ctx.addInputFileDependency(filePath);
// Map in file list file.
ErrorOr<std::unique_ptr<MemoryBuffer>> mb =
MemoryBuffer::getFileOrSTDIN(filePath);
if (std::error_code ec = mb.getError())
return ec;
StringRef buffer = mb->get()->getBuffer();
while (!buffer.empty()) {
// Split off each line in the file.
std::pair<StringRef, StringRef> lineAndRest = buffer.split('\n');
StringRef line = lineAndRest.first;
StringRef path;
if (!dirName.empty()) {
// If there is a <dir> then prepend dir to each line.
SmallString<256> fullPath;
fullPath.assign(dirName);
llvm::sys::path::append(fullPath, Twine(line));
path = ctx.copy(fullPath.str());
} else {
// No <dir> use whole line as input file path.
path = ctx.copy(line);
}
if (!ctx.pathExists(path)) {
return make_dynamic_error_code(Twine("File not found '")
+ path
+ "'");
}
if (ctx.testingFileUsage()) {
diagnostics << "Found filelist entry " << canonicalizePath(path) << '\n';
}
addFile(path, ctx, forceLoad, false, diagnostics);
buffer = lineAndRest.second;
}
return std::error_code();
}
/// Parse number assuming it is base 16, but allow 0x prefix.
static bool parseNumberBase16(StringRef numStr, uint64_t &baseAddress) {
if (numStr.startswith_lower("0x"))
numStr = numStr.drop_front(2);
return numStr.getAsInteger(16, baseAddress);
}
namespace lld {
bool DarwinLdDriver::linkMachO(int argc, const char *argv[],
raw_ostream &diagnostics) {
MachOLinkingContext ctx;
if (!parse(argc, argv, ctx, diagnostics))
return false;
if (ctx.doNothing())
return true;
return link(ctx, diagnostics);
}
bool DarwinLdDriver::parse(int argc, const char *argv[],
MachOLinkingContext &ctx, raw_ostream &diagnostics) {
// Parse command line options using DarwinLdOptions.td
std::unique_ptr<llvm::opt::InputArgList> parsedArgs;
DarwinLdOptTable table;
unsigned missingIndex;
unsigned missingCount;
bool globalWholeArchive = false;
parsedArgs.reset(
table.ParseArgs(&argv[1], &argv[argc], missingIndex, missingCount));
if (missingCount) {
diagnostics << "error: missing arg value for '"
<< parsedArgs->getArgString(missingIndex) << "' expected "
<< missingCount << " argument(s).\n";
return false;
}
for (auto unknownArg : parsedArgs->filtered(OPT_UNKNOWN)) {
diagnostics << "warning: ignoring unknown argument: "
<< unknownArg->getAsString(*parsedArgs) << "\n";
}
// Figure out output kind ( -dylib, -r, -bundle, -preload, or -static )
llvm::MachO::HeaderFileType fileType = llvm::MachO::MH_EXECUTE;
if ( llvm::opt::Arg *kind = parsedArgs->getLastArg(OPT_dylib, OPT_relocatable,
OPT_bundle, OPT_static, OPT_preload)) {
switch (kind->getOption().getID()) {
case OPT_dylib:
fileType = llvm::MachO::MH_DYLIB;
break;
case OPT_relocatable:
fileType = llvm::MachO::MH_OBJECT;
break;
case OPT_bundle:
fileType = llvm::MachO::MH_BUNDLE;
break;
case OPT_static:
fileType = llvm::MachO::MH_EXECUTE;
break;
case OPT_preload:
fileType = llvm::MachO::MH_PRELOAD;
break;
}
}
// Handle -arch xxx
MachOLinkingContext::Arch arch = MachOLinkingContext::arch_unknown;
if (llvm::opt::Arg *archStr = parsedArgs->getLastArg(OPT_arch)) {
arch = MachOLinkingContext::archFromName(archStr->getValue());
if (arch == MachOLinkingContext::arch_unknown) {
diagnostics << "error: unknown arch named '" << archStr->getValue()
<< "'\n";
return false;
}
}
// If no -arch specified, scan input files to find first non-fat .o file.
if (arch == MachOLinkingContext::arch_unknown) {
for (auto &inFile: parsedArgs->filtered(OPT_INPUT)) {
// This is expensive because it opens and maps the file. But that is
// ok because no -arch is rare.
if (MachOLinkingContext::isThinObjectFile(inFile->getValue(), arch))
break;
}
if (arch == MachOLinkingContext::arch_unknown
&& !parsedArgs->getLastArg(OPT_test_file_usage)) {
// If no -arch and no options at all, print usage message.
if (parsedArgs->size() == 0)
table.PrintHelp(llvm::outs(), argv[0], "LLVM Linker", false);
else
diagnostics << "error: -arch not specified and could not be inferred\n";
return false;
}
}
// Handle -macosx_version_min or -ios_version_min
MachOLinkingContext::OS os = MachOLinkingContext::OS::macOSX;
uint32_t minOSVersion = 0;
if (llvm::opt::Arg *minOS =
parsedArgs->getLastArg(OPT_macosx_version_min, OPT_ios_version_min,
OPT_ios_simulator_version_min)) {
switch (minOS->getOption().getID()) {
case OPT_macosx_version_min:
os = MachOLinkingContext::OS::macOSX;
if (MachOLinkingContext::parsePackedVersion(minOS->getValue(),
minOSVersion)) {
diagnostics << "error: malformed macosx_version_min value\n";
return false;
}
break;
case OPT_ios_version_min:
os = MachOLinkingContext::OS::iOS;
if (MachOLinkingContext::parsePackedVersion(minOS->getValue(),
minOSVersion)) {
diagnostics << "error: malformed ios_version_min value\n";
return false;
}
break;
case OPT_ios_simulator_version_min:
os = MachOLinkingContext::OS::iOS_simulator;
if (MachOLinkingContext::parsePackedVersion(minOS->getValue(),
minOSVersion)) {
diagnostics << "error: malformed ios_simulator_version_min value\n";
return false;
}
break;
}
} else {
// No min-os version on command line, check environment variables
}
// Now that there's enough information parsed in, let the linking context
// set up default values.
ctx.configure(fileType, arch, os, minOSVersion);
// Handle -e xxx
if (llvm::opt::Arg *entry = parsedArgs->getLastArg(OPT_entry))
ctx.setEntrySymbolName(entry->getValue());
// Handle -o xxx
if (llvm::opt::Arg *outpath = parsedArgs->getLastArg(OPT_output))
ctx.setOutputPath(outpath->getValue());
else
ctx.setOutputPath("a.out");
// Handle -image_base XXX and -seg1addr XXXX
if (llvm::opt::Arg *imageBase = parsedArgs->getLastArg(OPT_image_base)) {
uint64_t baseAddress;
if (parseNumberBase16(imageBase->getValue(), baseAddress)) {
diagnostics << "error: image_base expects a hex number\n";
return false;
} else if (baseAddress < ctx.pageZeroSize()) {
diagnostics << "error: image_base overlaps with __PAGEZERO\n";
return false;
} else if (baseAddress % ctx.pageSize()) {
diagnostics << "error: image_base must be a multiple of page size ("
<< llvm::format("0x%" PRIx64, ctx.pageSize()) << ")\n";
return false;
}
ctx.setBaseAddress(baseAddress);
}
// Handle -dead_strip
if (parsedArgs->getLastArg(OPT_dead_strip))
ctx.setDeadStripping(true);
// Handle -all_load
if (parsedArgs->getLastArg(OPT_all_load))
globalWholeArchive = true;
// Handle -install_name
if (llvm::opt::Arg *installName = parsedArgs->getLastArg(OPT_install_name))
ctx.setInstallName(installName->getValue());
else
ctx.setInstallName(ctx.outputPath());
// Handle -mark_dead_strippable_dylib
if (parsedArgs->getLastArg(OPT_mark_dead_strippable_dylib))
ctx.setDeadStrippableDylib(true);
// Handle -compatibility_version and -current_version
if (llvm::opt::Arg *vers =
parsedArgs->getLastArg(OPT_compatibility_version)) {
if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) {
diagnostics
<< "error: -compatibility_version can only be used with -dylib\n";
return false;
}
uint32_t parsedVers;
if (MachOLinkingContext::parsePackedVersion(vers->getValue(), parsedVers)) {
diagnostics << "error: -compatibility_version value is malformed\n";
return false;
}
ctx.setCompatibilityVersion(parsedVers);
}
if (llvm::opt::Arg *vers = parsedArgs->getLastArg(OPT_current_version)) {
if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) {
diagnostics << "-current_version can only be used with -dylib\n";
return false;
}
uint32_t parsedVers;
if (MachOLinkingContext::parsePackedVersion(vers->getValue(), parsedVers)) {
diagnostics << "error: -current_version value is malformed\n";
return false;
}
ctx.setCurrentVersion(parsedVers);
}
// Handle -bundle_loader
if (llvm::opt::Arg *loader = parsedArgs->getLastArg(OPT_bundle_loader))
ctx.setBundleLoader(loader->getValue());
// Handle -sectalign segname sectname align
for (auto &alignArg : parsedArgs->filtered(OPT_sectalign)) {
const char* segName = alignArg->getValue(0);
const char* sectName = alignArg->getValue(1);
const char* alignStr = alignArg->getValue(2);
if ((alignStr[0] == '0') && (alignStr[1] == 'x'))
alignStr += 2;
unsigned long long alignValue;
if (llvm::getAsUnsignedInteger(alignStr, 16, alignValue)) {
diagnostics << "error: -sectalign alignment value '"
<< alignStr << "' not a valid number\n";
return false;
}
uint8_t align2 = llvm::countTrailingZeros(alignValue);
if ( (unsigned long)(1 << align2) != alignValue ) {
diagnostics << "warning: alignment for '-sectalign "
<< segName << " " << sectName
<< llvm::format(" 0x%llX", alignValue)
<< "' is not a power of two, using "
<< llvm::format("0x%08X", (1 << align2)) << "\n";
}
ctx.addSectionAlignment(segName, sectName, align2);
}
// Handle -mllvm
for (auto &llvmArg : parsedArgs->filtered(OPT_mllvm)) {
ctx.appendLLVMOption(llvmArg->getValue());
}
// Handle -print_atoms
if (parsedArgs->getLastArg(OPT_print_atoms))
ctx.setPrintAtoms();
// Handle -t (trace) option.
if (parsedArgs->getLastArg(OPT_t))
ctx.setLogInputFiles(true);
// Handle -demangle option.
if (parsedArgs->getLastArg(OPT_demangle))
ctx.setDemangleSymbols(true);
// Handle -keep_private_externs
if (parsedArgs->getLastArg(OPT_keep_private_externs)) {
ctx.setKeepPrivateExterns(true);
if (ctx.outputMachOType() != llvm::MachO::MH_OBJECT)
diagnostics << "warning: -keep_private_externs only used in -r mode\n";
}
// Handle -dependency_info <path> used by Xcode.
if (llvm::opt::Arg *depInfo = parsedArgs->getLastArg(OPT_dependency_info)) {
if (std::error_code ec = ctx.createDependencyFile(depInfo->getValue())) {
diagnostics << "warning: " << ec.message()
<< ", processing '-dependency_info "
<< depInfo->getValue()
<< "'\n";
}
}
// In -test_file_usage mode, we'll be given an explicit list of paths that
// exist. We'll also be expected to print out information about how we located
// libraries and so on that the user specified, but not to actually do any
// linking.
if (parsedArgs->getLastArg(OPT_test_file_usage)) {
ctx.setTestingFileUsage();
// With paths existing by fiat, linking is not going to end well.
ctx.setDoNothing(true);
// Only bother looking for an existence override if we're going to use it.
for (auto existingPath : parsedArgs->filtered(OPT_path_exists)) {
ctx.addExistingPathForDebug(existingPath->getValue());
}
}
// Register possible input file parsers.
if (!ctx.doNothing()) {
ctx.registry().addSupportMachOObjects(ctx);
ctx.registry().addSupportArchives(ctx.logInputFiles());
ctx.registry().addSupportNativeObjects();
ctx.registry().addSupportYamlFiles();
}
// Now construct the set of library search directories, following ld64's
// baroque set of accumulated hacks. Mostly, the algorithm constructs
// { syslibroots } x { libpaths }
//
// Unfortunately, there are numerous exceptions:
// 1. Only absolute paths get modified by syslibroot options.
// 2. If there is just 1 -syslibroot, system paths not found in it are
// skipped.
// 3. If the last -syslibroot is "/", all of them are ignored entirely.
// 4. If { syslibroots } x path == {}, the original path is kept.
std::vector<StringRef> sysLibRoots;
for (auto syslibRoot : parsedArgs->filtered(OPT_syslibroot)) {
sysLibRoots.push_back(syslibRoot->getValue());
}
if (!sysLibRoots.empty()) {
// Ignore all if last -syslibroot is "/".
if (sysLibRoots.back() != "/")
ctx.setSysLibRoots(sysLibRoots);
}
// Paths specified with -L come first, and are not considered system paths for
// the case where there is precisely 1 -syslibroot.
for (auto libPath : parsedArgs->filtered(OPT_L)) {
ctx.addModifiedSearchDir(libPath->getValue());
}
// Process -F directories (where to look for frameworks).
for (auto fwPath : parsedArgs->filtered(OPT_F)) {
ctx.addFrameworkSearchDir(fwPath->getValue());
}
// -Z suppresses the standard search paths.
if (!parsedArgs->hasArg(OPT_Z)) {
ctx.addModifiedSearchDir("/usr/lib", true);
ctx.addModifiedSearchDir("/usr/local/lib", true);
ctx.addFrameworkSearchDir("/Library/Frameworks", true);
ctx.addFrameworkSearchDir("/System/Library/Frameworks", true);
}
// Now that we've constructed the final set of search paths, print out those
// search paths in verbose mode.
if (parsedArgs->getLastArg(OPT_v)) {
diagnostics << "Library search paths:\n";
for (auto path : ctx.searchDirs()) {
diagnostics << " " << path << '\n';
}
diagnostics << "Framework search paths:\n";
for (auto path : ctx.frameworkDirs()) {
diagnostics << " " << path << '\n';
}
}
// Handle -exported_symbols_list <file>
for (auto expFile : parsedArgs->filtered(OPT_exported_symbols_list)) {
if (ctx.exportMode() == MachOLinkingContext::ExportMode::blackList) {
diagnostics << "error: -exported_symbols_list cannot be combined "
<< "with -unexported_symbol[s_list]\n";
return false;
}
ctx.setExportMode(MachOLinkingContext::ExportMode::whiteList);
if (std::error_code ec = parseExportsList(expFile->getValue(), ctx,
diagnostics)) {
diagnostics << "error: " << ec.message()
<< ", processing '-exported_symbols_list "
<< expFile->getValue()
<< "'\n";
return false;
}
}
// Handle -exported_symbol <symbol>
for (auto symbol : parsedArgs->filtered(OPT_exported_symbol)) {
if (ctx.exportMode() == MachOLinkingContext::ExportMode::blackList) {
diagnostics << "error: -exported_symbol cannot be combined "
<< "with -unexported_symbol[s_list]\n";
return false;
}
ctx.setExportMode(MachOLinkingContext::ExportMode::whiteList);
ctx.addExportSymbol(symbol->getValue());
}
// Handle -unexported_symbols_list <file>
for (auto expFile : parsedArgs->filtered(OPT_unexported_symbols_list)) {
if (ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) {
diagnostics << "error: -unexported_symbols_list cannot be combined "
<< "with -exported_symbol[s_list]\n";
return false;
}
ctx.setExportMode(MachOLinkingContext::ExportMode::blackList);
if (std::error_code ec = parseExportsList(expFile->getValue(), ctx,
diagnostics)) {
diagnostics << "error: " << ec.message()
<< ", processing '-unexported_symbols_list "
<< expFile->getValue()
<< "'\n";
return false;
}
}
// Handle -unexported_symbol <symbol>
for (auto symbol : parsedArgs->filtered(OPT_unexported_symbol)) {
if (ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) {
diagnostics << "error: -unexported_symbol cannot be combined "
<< "with -exported_symbol[s_list]\n";
return false;
}
ctx.setExportMode(MachOLinkingContext::ExportMode::blackList);
ctx.addExportSymbol(symbol->getValue());
}
// Handle obosolete -multi_module and -single_module
if (llvm::opt::Arg *mod = parsedArgs->getLastArg(OPT_multi_module,
OPT_single_module)) {
if (mod->getOption().getID() == OPT_multi_module) {
diagnostics << "warning: -multi_module is obsolete and being ignored\n";
}
else {
if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) {
diagnostics << "warning: -single_module being ignored. "
"It is only for use when producing a dylib\n";
}
}
}
// Handle -pie or -no_pie
if (llvm::opt::Arg *pie = parsedArgs->getLastArg(OPT_pie, OPT_no_pie)) {
switch (ctx.outputMachOType()) {
case llvm::MachO::MH_EXECUTE:
switch (ctx.os()) {
case MachOLinkingContext::OS::macOSX:
if ((minOSVersion < 0x000A0500) &&
(pie->getOption().getID() == OPT_pie)) {
diagnostics << "-pie can only be used when targeting "
"Mac OS X 10.5 or later\n";
return false;
}
break;
case MachOLinkingContext::OS::iOS:
if ((minOSVersion < 0x00040200) &&
(pie->getOption().getID() == OPT_pie)) {
diagnostics << "-pie can only be used when targeting "
"iOS 4.2 or later\n";
return false;
}
break;
case MachOLinkingContext::OS::iOS_simulator:
if (pie->getOption().getID() == OPT_no_pie)
diagnostics << "iOS simulator programs must be built PIE\n";
return false;
break;
case MachOLinkingContext::OS::unknown:
break;
}
ctx.setPIE(pie->getOption().getID() == OPT_pie);
break;
case llvm::MachO::MH_PRELOAD:
break;
case llvm::MachO::MH_DYLIB:
case llvm::MachO::MH_BUNDLE:
diagnostics << "warning: " << pie->getSpelling() << " being ignored. "
<< "It is only used when linking main executables\n";
break;
default:
diagnostics << pie->getSpelling()
<< " can only used when linking main executables\n";
return false;
break;
}
}
// Handle debug info handling options: -S
if (parsedArgs->hasArg(OPT_S))
ctx.setDebugInfoMode(MachOLinkingContext::DebugInfoMode::noDebugMap);
// Handle -order_file <file>
for (auto orderFile : parsedArgs->filtered(OPT_order_file)) {
if (std::error_code ec = parseOrderFile(orderFile->getValue(), ctx,
diagnostics)) {
diagnostics << "error: " << ec.message()
<< ", processing '-order_file "
<< orderFile->getValue()
<< "'\n";
return false;
}
}
// Handle -rpath <path>
if (parsedArgs->hasArg(OPT_rpath)) {
switch (ctx.outputMachOType()) {
case llvm::MachO::MH_EXECUTE:
case llvm::MachO::MH_DYLIB:
case llvm::MachO::MH_BUNDLE:
if (!ctx.minOS("10.5", "2.0")) {
if (ctx.os() == MachOLinkingContext::OS::macOSX) {
diagnostics << "error: -rpath can only be used when targeting "
"OS X 10.5 or later\n";
} else {
diagnostics << "error: -rpath can only be used when targeting "
"iOS 2.0 or later\n";
}
return false;
}
break;
default:
diagnostics << "error: -rpath can only be used when creating "
"a dynamic final linked image\n";
return false;
}
for (auto rPath : parsedArgs->filtered(OPT_rpath)) {
ctx.addRpath(rPath->getValue());
}
}
// Handle input files
for (auto &arg : *parsedArgs) {
bool upward;
ErrorOr<StringRef> resolvedPath = StringRef();
switch (arg->getOption().getID()) {
default:
continue;
case OPT_INPUT:
addFile(arg->getValue(), ctx, globalWholeArchive, false, diagnostics);
break;
case OPT_upward_library:
addFile(arg->getValue(), ctx, false, true, diagnostics);
break;
case OPT_force_load:
addFile(arg->getValue(), ctx, true, false, diagnostics);
break;
case OPT_l:
case OPT_upward_l:
upward = (arg->getOption().getID() == OPT_upward_l);
resolvedPath = ctx.searchLibrary(arg->getValue());
if (!resolvedPath) {
diagnostics << "Unable to find library for " << arg->getSpelling()
<< arg->getValue() << "\n";
return false;
} else if (ctx.testingFileUsage()) {
diagnostics << "Found " << (upward ? "upward " : " ") << "library "
<< canonicalizePath(resolvedPath.get()) << '\n';
}
addFile(resolvedPath.get(), ctx, globalWholeArchive, upward, diagnostics);
break;
case OPT_framework:
case OPT_upward_framework:
upward = (arg->getOption().getID() == OPT_upward_framework);
resolvedPath = ctx.findPathForFramework(arg->getValue());
if (!resolvedPath) {
diagnostics << "Unable to find framework for "
<< arg->getSpelling() << " " << arg->getValue() << "\n";
return false;
} else if (ctx.testingFileUsage()) {
diagnostics << "Found " << (upward ? "upward " : " ") << "framework "
<< canonicalizePath(resolvedPath.get()) << '\n';
}
addFile(resolvedPath.get(), ctx, globalWholeArchive, upward, diagnostics);
break;
case OPT_filelist:
if (std::error_code ec = loadFileList(arg->getValue(),
ctx, globalWholeArchive,
diagnostics)) {
diagnostics << "error: " << ec.message()
<< ", processing '-filelist " << arg->getValue()
<< "'\n";
return false;
}
break;
}
}
if (ctx.getNodes().empty()) {
diagnostics << "No input files\n";
return false;
}
// Validate the combination of options used.
return ctx.validate(diagnostics);
}
} // namespace lld

View file

@ -0,0 +1,187 @@
include "llvm/Option/OptParser.td"
// output kinds
def grp_kind : OptionGroup<"outs">, HelpText<"OUTPUT KIND">;
def relocatable : Flag<["-"], "r">,
HelpText<"Create relocatable object file">, Group<grp_kind>;
def static : Flag<["-"], "static">,
HelpText<"Create static executable">, Group<grp_kind>;
def dynamic : Flag<["-"], "dynamic">,
HelpText<"Create dynamic executable (default)">,Group<grp_kind>;
def dylib : Flag<["-"], "dylib">,
HelpText<"Create dynamic library">, Group<grp_kind>;
def bundle : Flag<["-"], "bundle">,
HelpText<"Create dynamic bundle">, Group<grp_kind>;
def execute : Flag<["-"], "execute">,
HelpText<"Create main executable (default)">, Group<grp_kind>;
def preload : Flag<["-"], "preload">,
HelpText<"Create binary for use with embedded systems">, Group<grp_kind>;
// optimizations
def grp_opts : OptionGroup<"opts">, HelpText<"OPTIMIZATIONS">;
def dead_strip : Flag<["-"], "dead_strip">,
HelpText<"Remove unreference code and data">, Group<grp_opts>;
def macosx_version_min : Separate<["-"], "macosx_version_min">,
MetaVarName<"<version>">,
HelpText<"Minimum Mac OS X version">, Group<grp_opts>;
def ios_version_min : Separate<["-"], "ios_version_min">,
MetaVarName<"<version>">,
HelpText<"Minimum iOS version">, Group<grp_opts>;
def iphoneos_version_min : Separate<["-"], "iphoneos_version_min">,
Alias<ios_version_min>;
def ios_simulator_version_min : Separate<["-"], "ios_simulator_version_min">,
MetaVarName<"<version>">,
HelpText<"Minimum iOS simulator version">, Group<grp_opts>;
def mllvm : Separate<["-"], "mllvm">,
MetaVarName<"<option>">,
HelpText<"Options to pass to LLVM during LTO">, Group<grp_opts>;
def exported_symbols_list : Separate<["-"], "exported_symbols_list">,
MetaVarName<"<file-path>">,
HelpText<"Restricts which symbols will be exported">, Group<grp_opts>;
def exported_symbol : Separate<["-"], "exported_symbol">,
MetaVarName<"<symbol>">,
HelpText<"Restricts which symbols will be exported">, Group<grp_opts>;
def unexported_symbols_list : Separate<["-"], "unexported_symbols_list">,
MetaVarName<"<file-path>">,
HelpText<"Lists symbols that should not be exported">, Group<grp_opts>;
def unexported_symbol : Separate<["-"], "unexported_symbol">,
MetaVarName<"<symbol>">,
HelpText<"A symbol which should not be exported">, Group<grp_opts>;
def keep_private_externs : Flag<["-"], "keep_private_externs">,
HelpText<"Private extern (hidden) symbols should not be transformed "
"into local symbols">, Group<grp_opts>;
def order_file : Separate<["-"], "order_file">,
MetaVarName<"<file-path>">,
HelpText<"re-order and move specified symbols to start of their section">,
Group<grp_opts>;
// main executable options
def grp_main : OptionGroup<"opts">, HelpText<"MAIN EXECUTABLE OPTIONS">;
def entry : Separate<["-"], "e">,
MetaVarName<"<entry-name>">,
HelpText<"entry symbol name">,Group<grp_main>;
def pie : Flag<["-"], "pie">,
HelpText<"Create Position Independent Executable (for ASLR)">,
Group<grp_main>;
def no_pie : Flag<["-"], "no_pie">,
HelpText<"Do not create Position Independent Executable">,
Group<grp_main>;
// dylib executable options
def grp_dylib : OptionGroup<"opts">, HelpText<"DYLIB EXECUTABLE OPTIONS">;
def install_name : Separate<["-"], "install_name">,
MetaVarName<"<path>">,
HelpText<"The dylib's install name">, Group<grp_dylib>;
def mark_dead_strippable_dylib : Flag<["-"], "mark_dead_strippable_dylib">,
HelpText<"Marks the dylib as having no side effects during initialization">,
Group<grp_dylib>;
def compatibility_version : Separate<["-"], "compatibility_version">,
MetaVarName<"<version>">,
HelpText<"The dylib's compatibility version">, Group<grp_dylib>;
def current_version : Separate<["-"], "current_version">,
MetaVarName<"<version>">,
HelpText<"The dylib's current version">, Group<grp_dylib>;
// dylib executable options - compatibility aliases
def dylib_install_name : Separate<["-"], "dylib_install_name">,
Alias<install_name>;
def dylib_compatibility_version : Separate<["-"], "dylib_compatibility_version">,
MetaVarName<"<version>">, Alias<compatibility_version>;
def dylib_current_version : Separate<["-"], "dylib_current_version">,
MetaVarName<"<version>">, Alias<current_version>;
// bundle executable options
def grp_bundle : OptionGroup<"opts">, HelpText<"BUNDLE EXECUTABLE OPTIONS">;
def bundle_loader : Separate<["-"], "bundle_loader">,
MetaVarName<"<path>">,
HelpText<"The executable that will be loading this Mach-O bundle">,
Group<grp_bundle>;
// library options
def grp_libs : OptionGroup<"libs">, HelpText<"LIBRARY OPTIONS">;
def L : JoinedOrSeparate<["-"], "L">,
MetaVarName<"<dir>">,
HelpText<"Add directory to library search path">, Group<grp_libs>;
def F : JoinedOrSeparate<["-"], "F">,
MetaVarName<"<dir>">,
HelpText<"Add directory to framework search path">, Group<grp_libs>;
def Z : Flag<["-"], "Z">,
HelpText<"Do not search standard directories for libraries or frameworks">;
def all_load : Flag<["-"], "all_load">,
HelpText<"Forces all members of all static libraries to be loaded">,
Group<grp_libs>;
def force_load : Separate<["-"], "force_load">,
MetaVarName<"<library-path>">,
HelpText<"Forces all members of specified static libraries to be loaded">,
Group<grp_libs>;
def syslibroot : Separate<["-"], "syslibroot">, MetaVarName<"<dir>">,
HelpText<"Add path to SDK to all absolute library search paths">,
Group<grp_libs>;
// Input options
def l : Joined<["-"], "l">,
MetaVarName<"<libname>">,
HelpText<"Base name of library searched for in -L directories">;
def upward_l : Joined<["-"], "upward-l">,
MetaVarName<"<libname>">,
HelpText<"Base name of upward library searched for in -L directories">;
def framework : Separate<["-"], "framework">,
MetaVarName<"<name>">,
HelpText<"Base name of framework searched for in -F directories">;
def upward_framework : Separate<["-"], "upward_framework">,
MetaVarName<"<name>">,
HelpText<"Base name of upward framework searched for in -F directories">;
def upward_library : Separate<["-"], "upward_library">,
MetaVarName<"<path>">,
HelpText<"path to upward dylib to link with">;
def filelist : Separate<["-"], "filelist">,
MetaVarName<"<path>">,
HelpText<"file containing paths to input files">;
// test case options
def print_atoms : Flag<["-"], "print_atoms">,
HelpText<"Emit output as yaml atoms">;
def test_file_usage : Flag<["-"], "test_file_usage">,
HelpText<"Only files specified by -file_exists are considered to exist. "
"Print which files would be used">;
def path_exists : Separate<["-"], "path_exists">,
MetaVarName<"<path>">,
HelpText<"Used with -test_file_usage to declare a path">;
// general options
def output : Separate<["-"], "o">,
MetaVarName<"<path>">,
HelpText<"Output file path">;
def arch : Separate<["-"], "arch">,
MetaVarName<"<arch-name>">,
HelpText<"Architecture to link">;
def sectalign : MultiArg<["-"], "sectalign", 3>,
MetaVarName<"<segname> <sectname> <alignment>">,
HelpText<"alignment for segment/section">;
def image_base : Separate<["-"], "image_base">;
def seg1addr : Separate<["-"], "seg1addr">, Alias<image_base>;
def demangle : Flag<["-"], "demangle">,
HelpText<"Demangles symbol names in errors and warnings">;
def dependency_info : Separate<["-"], "dependency_info">,
MetaVarName<"<file>">,
HelpText<"Write binary list of files used during link">;
def S : Flag<["-"], "S">,
HelpText<"Remove debug information (STABS or DWARF) from the output file">;
def rpath : Separate<["-"], "rpath">,
MetaVarName<"<path>">,
HelpText<"Add path to the runpath search path list for image being created">;
def t : Flag<["-"], "t">,
HelpText<"Print the names of the input files as ld processes them">;
def v : Flag<["-"], "v">,
HelpText<"Print linker information">;
// Obsolete options
def grp_obsolete : OptionGroup<"obsolete">, HelpText<"OBSOLETE OPTIONS">;
def single_module : Flag<["-"], "single_module">,
HelpText<"Default for dylibs">, Group<grp_obsolete>;
def multi_module : Flag<["-"], "multi_module">,
HelpText<"Unsupported way to build dylibs">, Group<grp_obsolete>;

130
lib/Driver/Driver.cpp Normal file
View file

@ -0,0 +1,130 @@
//===- lib/Driver/Driver.cpp - Linker Driver Emulator ---------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lld/Core/ArchiveLibraryFile.h"
#include "lld/Core/File.h"
#include "lld/Core/Instrumentation.h"
#include "lld/Core/LLVM.h"
#include "lld/Core/Parallel.h"
#include "lld/Core/PassManager.h"
#include "lld/Core/Reader.h"
#include "lld/Core/Resolver.h"
#include "lld/Core/Writer.h"
#include "lld/Driver/Driver.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Option/Arg.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/raw_ostream.h"
#include <mutex>
namespace lld {
FileVector makeErrorFile(StringRef path, std::error_code ec) {
std::vector<std::unique_ptr<File>> result;
result.push_back(llvm::make_unique<ErrorFile>(path, ec));
return result;
}
FileVector parseMemberFiles(FileVector &files) {
std::vector<std::unique_ptr<File>> members;
for (std::unique_ptr<File> &file : files) {
if (auto *archive = dyn_cast<ArchiveLibraryFile>(file.get())) {
if (std::error_code ec = archive->parseAllMembers(members))
return makeErrorFile(file->path(), ec);
} else {
members.push_back(std::move(file));
}
}
return members;
}
FileVector loadFile(LinkingContext &ctx, StringRef path, bool wholeArchive) {
ErrorOr<std::unique_ptr<MemoryBuffer>> mb
= MemoryBuffer::getFileOrSTDIN(path);
if (std::error_code ec = mb.getError())
return makeErrorFile(path, ec);
std::vector<std::unique_ptr<File>> files;
if (std::error_code ec = ctx.registry().loadFile(std::move(mb.get()), files))
return makeErrorFile(path, ec);
if (wholeArchive)
return parseMemberFiles(files);
return files;
}
/// This is where the link is actually performed.
bool Driver::link(LinkingContext &context, raw_ostream &diagnostics) {
// Honor -mllvm
if (!context.llvmOptions().empty()) {
unsigned numArgs = context.llvmOptions().size();
const char **args = new const char *[numArgs + 2];
args[0] = "lld (LLVM option parsing)";
for (unsigned i = 0; i != numArgs; ++i)
args[i + 1] = context.llvmOptions()[i];
args[numArgs + 1] = 0;
llvm::cl::ParseCommandLineOptions(numArgs + 1, args);
}
if (context.getNodes().empty())
return false;
for (std::unique_ptr<Node> &ie : context.getNodes())
if (FileNode *node = dyn_cast<FileNode>(ie.get()))
context.getTaskGroup().spawn([node] { node->getFile()->parse(); });
std::vector<std::unique_ptr<File>> internalFiles;
context.createInternalFiles(internalFiles);
for (auto i = internalFiles.rbegin(), e = internalFiles.rend(); i != e; ++i) {
auto &members = context.getNodes();
members.insert(members.begin(), llvm::make_unique<FileNode>(std::move(*i)));
}
// Give target a chance to add files.
std::vector<std::unique_ptr<File>> implicitFiles;
context.createImplicitFiles(implicitFiles);
for (auto i = implicitFiles.rbegin(), e = implicitFiles.rend(); i != e; ++i) {
auto &members = context.getNodes();
members.insert(members.begin(), llvm::make_unique<FileNode>(std::move(*i)));
}
// Give target a chance to postprocess input files.
// Mach-O uses this chance to move all object files before library files.
// ELF adds specific undefined symbols resolver.
context.finalizeInputFiles();
// Do core linking.
ScopedTask resolveTask(getDefaultDomain(), "Resolve");
Resolver resolver(context);
if (!resolver.resolve())
return false;
std::unique_ptr<MutableFile> merged = resolver.resultFile();
resolveTask.end();
// Run passes on linked atoms.
ScopedTask passTask(getDefaultDomain(), "Passes");
PassManager pm;
context.addPasses(pm);
pm.runOnFile(merged);
passTask.end();
// Give linked atoms to Writer to generate output file.
ScopedTask writeTask(getDefaultDomain(), "Write");
if (std::error_code ec = context.writeFile(*merged)) {
diagnostics << "Failed to write file '" << context.outputPath()
<< "': " << ec.message() << "\n";
return false;
}
return true;
}
} // namespace

760
lib/Driver/GnuLdDriver.cpp Normal file
View file

@ -0,0 +1,760 @@
//===- lib/Driver/GnuLdDriver.cpp -----------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// Concrete instance of the Driver for GNU's ld.
///
//===----------------------------------------------------------------------===//
#include "lld/Driver/Driver.h"
#include "lld/ReaderWriter/ELFLinkingContext.h"
#include "lld/ReaderWriter/ELFTargets.h"
#include "lld/ReaderWriter/LinkerScript.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
#include <cstring>
#include <tuple>
using namespace lld;
using llvm::BumpPtrAllocator;
namespace {
// Create enum with OPT_xxx values for each option in GnuLdOptions.td
enum {
OPT_INVALID = 0,
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELP, META) \
OPT_##ID,
#include "GnuLdOptions.inc"
#undef OPTION
};
// Create prefix string literals used in GnuLdOptions.td
#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
#include "GnuLdOptions.inc"
#undef PREFIX
// Create table mapping all options defined in GnuLdOptions.td
static const llvm::opt::OptTable::Info infoTable[] = {
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR) \
{ PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \
PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS },
#include "GnuLdOptions.inc"
#undef OPTION
};
// Create OptTable class for parsing actual command line arguments
class GnuLdOptTable : public llvm::opt::OptTable {
public:
GnuLdOptTable() : OptTable(infoTable, llvm::array_lengthof(infoTable)){}
};
class DriverStringSaver : public llvm::cl::StringSaver {
public:
DriverStringSaver(BumpPtrAllocator &alloc) : _alloc(alloc) {}
const char *SaveString(const char *s) override {
char *p = _alloc.Allocate<char>(strlen(s) + 1);
strcpy(p, s);
return p;
}
private:
BumpPtrAllocator &_alloc;
};
} // anonymous namespace
// If a command line option starts with "@", the driver reads its suffix as a
// file, parse its contents as a list of command line options, and insert them
// at the original @file position. If file cannot be read, @file is not expanded
// and left unmodified. @file can appear in a response file, so it's a recursive
// process.
static std::tuple<int, const char **>
maybeExpandResponseFiles(int argc, const char **argv, BumpPtrAllocator &alloc) {
// Expand response files.
SmallVector<const char *, 256> smallvec;
for (int i = 0; i < argc; ++i)
smallvec.push_back(argv[i]);
DriverStringSaver saver(alloc);
llvm::cl::ExpandResponseFiles(saver, llvm::cl::TokenizeGNUCommandLine, smallvec);
// Pack the results to a C-array and return it.
argc = smallvec.size();
const char **copy = alloc.Allocate<const char *>(argc + 1);
std::copy(smallvec.begin(), smallvec.end(), copy);
copy[argc] = nullptr;
return std::make_tuple(argc, copy);
}
static std::error_code
getFileMagic(StringRef path, llvm::sys::fs::file_magic &magic) {
std::error_code ec = llvm::sys::fs::identify_magic(path, magic);
if (ec)
return ec;
switch (magic) {
case llvm::sys::fs::file_magic::archive:
case llvm::sys::fs::file_magic::elf_relocatable:
case llvm::sys::fs::file_magic::elf_shared_object:
case llvm::sys::fs::file_magic::unknown:
return std::error_code();
default:
return make_dynamic_error_code(StringRef("unknown type of object file"));
}
}
// Parses an argument of --defsym=<sym>=<number>
static bool parseDefsymAsAbsolute(StringRef opt, StringRef &sym,
uint64_t &addr) {
size_t equalPos = opt.find('=');
if (equalPos == 0 || equalPos == StringRef::npos)
return false;
sym = opt.substr(0, equalPos);
if (opt.substr(equalPos + 1).getAsInteger(0, addr))
return false;
return true;
}
// Parses an argument of --defsym=<sym>=<sym>
static bool parseDefsymAsAlias(StringRef opt, StringRef &sym,
StringRef &target) {
size_t equalPos = opt.find('=');
if (equalPos == 0 || equalPos == StringRef::npos)
return false;
sym = opt.substr(0, equalPos);
target = opt.substr(equalPos + 1);
return !target.empty();
}
// Parses -z max-page-size=<value>
static bool parseMaxPageSize(StringRef opt, uint64_t &val) {
size_t equalPos = opt.find('=');
if (equalPos == 0 || equalPos == StringRef::npos)
return false;
StringRef value = opt.substr(equalPos + 1);
val = 0;
if (value.getAsInteger(0, val) || !val)
return false;
return true;
}
bool GnuLdDriver::linkELF(int argc, const char *argv[], raw_ostream &diag) {
BumpPtrAllocator alloc;
std::tie(argc, argv) = maybeExpandResponseFiles(argc, argv, alloc);
std::unique_ptr<ELFLinkingContext> options;
if (!parse(argc, argv, options, diag))
return false;
if (!options)
return true;
bool linked = link(*options, diag);
// Handle --stats.
if (options->collectStats()) {
llvm::TimeRecord t = llvm::TimeRecord::getCurrentTime(true);
diag << "total time in link " << t.getProcessTime() << "\n";
diag << "data size " << t.getMemUsed() << "\n";
}
return linked;
}
static llvm::Optional<llvm::Triple::ArchType>
getArchType(const llvm::Triple &triple, StringRef value) {
switch (triple.getArch()) {
case llvm::Triple::x86:
case llvm::Triple::x86_64:
if (value == "elf_i386")
return llvm::Triple::x86;
if (value == "elf_x86_64")
return llvm::Triple::x86_64;
return llvm::None;
case llvm::Triple::mipsel:
case llvm::Triple::mips64el:
if (value == "elf32ltsmip")
return llvm::Triple::mipsel;
if (value == "elf64ltsmip")
return llvm::Triple::mips64el;
return llvm::None;
case llvm::Triple::aarch64:
if (value == "aarch64linux")
return llvm::Triple::aarch64;
return llvm::None;
case llvm::Triple::arm:
if (value == "armelf_linux_eabi")
return llvm::Triple::arm;
return llvm::None;
default:
return llvm::None;
}
}
static bool isLinkerScript(StringRef path, raw_ostream &diag) {
llvm::sys::fs::file_magic magic = llvm::sys::fs::file_magic::unknown;
std::error_code ec = getFileMagic(path, magic);
if (ec) {
diag << "unknown input file format for file " << path << "\n";
return false;
}
return magic == llvm::sys::fs::file_magic::unknown;
}
static ErrorOr<StringRef>
findFile(ELFLinkingContext &ctx, StringRef path, bool dashL) {
// If the path was referred to by using a -l argument, let's search
// for the file in the search path.
if (dashL) {
ErrorOr<StringRef> pathOrErr = ctx.searchLibrary(path);
if (std::error_code ec = pathOrErr.getError())
return make_dynamic_error_code(
Twine("Unable to find library -l") + path + ": " + ec.message());
path = pathOrErr.get();
}
if (!llvm::sys::fs::exists(path))
return make_dynamic_error_code(
Twine("lld: cannot find file ") + path);
return path;
}
static bool isPathUnderSysroot(StringRef sysroot, StringRef path) {
if (sysroot.empty())
return false;
while (!path.empty() && !llvm::sys::fs::equivalent(sysroot, path))
path = llvm::sys::path::parent_path(path);
return !path.empty();
}
static std::error_code
addFilesFromLinkerScript(ELFLinkingContext &ctx, StringRef scriptPath,
const std::vector<script::Path> &inputPaths,
raw_ostream &diag) {
bool sysroot = (!ctx.getSysroot().empty()
&& isPathUnderSysroot(ctx.getSysroot(), scriptPath));
for (const script::Path &path : inputPaths) {
ErrorOr<StringRef> pathOrErr = path._isDashlPrefix
? ctx.searchLibrary(path._path) : ctx.searchFile(path._path, sysroot);
if (std::error_code ec = pathOrErr.getError()) {
auto file = llvm::make_unique<ErrorFile>(path._path, ec);
ctx.getNodes().push_back(llvm::make_unique<FileNode>(std::move(file)));
continue;
}
std::vector<std::unique_ptr<File>> files
= loadFile(ctx, pathOrErr.get(), false);
for (std::unique_ptr<File> &file : files) {
if (ctx.logInputFiles())
diag << file->path() << "\n";
ctx.getNodes().push_back(llvm::make_unique<FileNode>(std::move(file)));
}
}
return std::error_code();
}
std::error_code GnuLdDriver::evalLinkerScript(ELFLinkingContext &ctx,
std::unique_ptr<MemoryBuffer> mb,
raw_ostream &diag,
bool nostdlib) {
// Read the script file from disk and parse.
StringRef path = mb->getBufferIdentifier();
auto parser = llvm::make_unique<script::Parser>(std::move(mb));
if (std::error_code ec = parser->parse())
return ec;
script::LinkerScript *script = parser->get();
if (!script)
return LinkerScriptReaderError::parse_error;
// Evaluate script commands.
// Currently we only recognize this subset of linker script commands.
for (const script::Command *c : script->_commands) {
if (auto *input = dyn_cast<script::Input>(c))
if (std::error_code ec = addFilesFromLinkerScript(
ctx, path, input->getPaths(), diag))
return ec;
if (auto *group = dyn_cast<script::Group>(c)) {
int origSize = ctx.getNodes().size();
if (std::error_code ec = addFilesFromLinkerScript(
ctx, path, group->getPaths(), diag))
return ec;
size_t groupSize = ctx.getNodes().size() - origSize;
ctx.getNodes().push_back(llvm::make_unique<GroupEnd>(groupSize));
}
if (auto *searchDir = dyn_cast<script::SearchDir>(c))
if (!nostdlib)
ctx.addSearchPath(searchDir->getSearchPath());
if (auto *entry = dyn_cast<script::Entry>(c))
ctx.setEntrySymbolName(entry->getEntryName());
if (auto *output = dyn_cast<script::Output>(c))
ctx.setOutputPath(output->getOutputFileName());
if (auto *externs = dyn_cast<script::Extern>(c)) {
for (auto symbol : *externs) {
ctx.addInitialUndefinedSymbol(symbol);
}
}
}
// Transfer ownership of the script to the linking context
ctx.linkerScriptSema().addLinkerScript(std::move(parser));
return std::error_code();
}
bool GnuLdDriver::applyEmulation(llvm::Triple &triple,
llvm::opt::InputArgList &args,
raw_ostream &diag) {
llvm::opt::Arg *arg = args.getLastArg(OPT_m);
if (!arg)
return true;
llvm::Optional<llvm::Triple::ArchType> arch =
getArchType(triple, arg->getValue());
if (!arch) {
diag << "error: unsupported emulation '" << arg->getValue() << "'.\n";
return false;
}
triple.setArch(*arch);
return true;
}
void GnuLdDriver::addPlatformSearchDirs(ELFLinkingContext &ctx,
llvm::Triple &triple,
llvm::Triple &baseTriple) {
if (triple.getOS() == llvm::Triple::NetBSD &&
triple.getArch() == llvm::Triple::x86 &&
baseTriple.getArch() == llvm::Triple::x86_64) {
ctx.addSearchPath("=/usr/lib/i386");
return;
}
ctx.addSearchPath("=/usr/lib");
}
std::unique_ptr<ELFLinkingContext>
GnuLdDriver::createELFLinkingContext(llvm::Triple triple) {
std::unique_ptr<ELFLinkingContext> p;
// FIXME: #include "llvm/Config/Targets.def"
#define LLVM_TARGET(targetName) \
if ((p = elf::targetName##LinkingContext::create(triple))) return p;
LLVM_TARGET(AArch64)
LLVM_TARGET(ARM)
LLVM_TARGET(Hexagon)
LLVM_TARGET(Mips)
LLVM_TARGET(X86)
LLVM_TARGET(Example)
LLVM_TARGET(X86_64)
#undef LLVM_TARGET
return nullptr;
}
static llvm::Optional<bool>
getBool(const llvm::opt::InputArgList &parsedArgs,
unsigned yesFlag, unsigned noFlag) {
if (auto *arg = parsedArgs.getLastArg(yesFlag, noFlag))
return arg->getOption().getID() == yesFlag;
return llvm::None;
}
bool GnuLdDriver::parse(int argc, const char *argv[],
std::unique_ptr<ELFLinkingContext> &context,
raw_ostream &diag) {
// Parse command line options using GnuLdOptions.td
std::unique_ptr<llvm::opt::InputArgList> parsedArgs;
GnuLdOptTable table;
unsigned missingIndex;
unsigned missingCount;
parsedArgs.reset(
table.ParseArgs(&argv[1], &argv[argc], missingIndex, missingCount));
if (missingCount) {
diag << "error: missing arg value for '"
<< parsedArgs->getArgString(missingIndex) << "' expected "
<< missingCount << " argument(s).\n";
return false;
}
// Handle --help
if (parsedArgs->hasArg(OPT_help)) {
table.PrintHelp(llvm::outs(), argv[0], "LLVM Linker", false);
return true;
}
// Use -target or use default target triple to instantiate LinkingContext
llvm::Triple baseTriple;
if (auto *arg = parsedArgs->getLastArg(OPT_target)) {
baseTriple = llvm::Triple(arg->getValue());
} else {
baseTriple = getDefaultTarget(argv[0]);
}
llvm::Triple triple(baseTriple);
if (!applyEmulation(triple, *parsedArgs, diag))
return false;
std::unique_ptr<ELFLinkingContext> ctx(createELFLinkingContext(triple));
if (!ctx) {
diag << "unknown target triple\n";
return false;
}
// Copy mllvm
for (auto *arg : parsedArgs->filtered(OPT_mllvm))
ctx->appendLLVMOption(arg->getValue());
// Ignore unknown arguments.
for (auto unknownArg : parsedArgs->filtered(OPT_UNKNOWN))
diag << "warning: ignoring unknown argument: "
<< unknownArg->getValue() << "\n";
// Set sys root path.
if (auto *arg = parsedArgs->getLastArg(OPT_sysroot))
ctx->setSysroot(arg->getValue());
// Handle --demangle option(For compatibility)
if (parsedArgs->hasArg(OPT_demangle))
ctx->setDemangleSymbols(true);
// Handle --no-demangle option.
if (parsedArgs->hasArg(OPT_no_demangle))
ctx->setDemangleSymbols(false);
// Figure out output kind (-r, -static, -shared)
if (parsedArgs->hasArg(OPT_relocatable)) {
ctx->setOutputELFType(llvm::ELF::ET_REL);
ctx->setPrintRemainingUndefines(false);
ctx->setAllowRemainingUndefines(true);
}
if (parsedArgs->hasArg(OPT_static)) {
ctx->setOutputELFType(llvm::ELF::ET_EXEC);
ctx->setIsStaticExecutable(true);
}
if (parsedArgs->hasArg(OPT_shared)) {
ctx->setOutputELFType(llvm::ELF::ET_DYN);
ctx->setAllowShlibUndefines(true);
ctx->setUseShlibUndefines(false);
ctx->setPrintRemainingUndefines(false);
ctx->setAllowRemainingUndefines(true);
}
// Handle --stats.
if (parsedArgs->hasArg(OPT_stats)) {
ctx->setCollectStats(true);
}
// Figure out if the output type is nmagic/omagic
if (auto *arg = parsedArgs->getLastArg(
OPT_nmagic, OPT_omagic, OPT_no_omagic)) {
switch (arg->getOption().getID()) {
case OPT_nmagic:
ctx->setOutputMagic(ELFLinkingContext::OutputMagic::NMAGIC);
ctx->setIsStaticExecutable(true);
break;
case OPT_omagic:
ctx->setOutputMagic(ELFLinkingContext::OutputMagic::OMAGIC);
ctx->setIsStaticExecutable(true);
break;
case OPT_no_omagic:
ctx->setOutputMagic(ELFLinkingContext::OutputMagic::DEFAULT);
ctx->setNoAllowDynamicLibraries();
break;
}
}
if (parsedArgs->hasArg(OPT_strip_all))
ctx->setStripSymbols(true);
if (auto *arg = parsedArgs->getLastArg(OPT_soname))
ctx->setSharedObjectName(arg->getValue());
if (parsedArgs->hasArg(OPT_rosegment))
ctx->setCreateSeparateROSegment();
if (parsedArgs->hasArg(OPT_no_align_segments))
ctx->setAlignSegments(false);
if (auto *arg = parsedArgs->getLastArg(OPT_image_base)) {
uint64_t baseAddress = 0;
StringRef inputValue = arg->getValue();
if (inputValue.getAsInteger(0, baseAddress) || !baseAddress) {
diag << "invalid value for image base " << inputValue << "\n";
return false;
}
ctx->setBaseAddress(baseAddress);
}
if (parsedArgs->hasArg(OPT_merge_strings))
ctx->setMergeCommonStrings(true);
if (parsedArgs->hasArg(OPT_t))
ctx->setLogInputFiles(true);
if (parsedArgs->hasArg(OPT_use_shlib_undefs))
ctx->setUseShlibUndefines(true);
if (auto val = getBool(*parsedArgs, OPT_allow_shlib_undefs,
OPT_no_allow_shlib_undefs))
ctx->setAllowShlibUndefines(*val);
if (auto *arg = parsedArgs->getLastArg(OPT_e))
ctx->setEntrySymbolName(arg->getValue());
if (auto *arg = parsedArgs->getLastArg(OPT_output))
ctx->setOutputPath(arg->getValue());
if (parsedArgs->hasArg(OPT_noinhibit_exec))
ctx->setAllowRemainingUndefines(true);
if (auto val = getBool(*parsedArgs, OPT_export_dynamic,
OPT_no_export_dynamic))
ctx->setExportDynamic(*val);
if (parsedArgs->hasArg(OPT_allow_multiple_definition))
ctx->setAllowDuplicates(true);
if (auto *arg = parsedArgs->getLastArg(OPT_dynamic_linker))
ctx->setInterpreter(arg->getValue());
if (auto *arg = parsedArgs->getLastArg(OPT_init))
ctx->setInitFunction(arg->getValue());
if (auto *arg = parsedArgs->getLastArg(OPT_fini))
ctx->setFiniFunction(arg->getValue());
if (auto *arg = parsedArgs->getLastArg(OPT_output_filetype))
ctx->setOutputFileType(arg->getValue());
for (auto *arg : parsedArgs->filtered(OPT_L))
ctx->addSearchPath(arg->getValue());
// Add the default search directory specific to the target.
if (!parsedArgs->hasArg(OPT_nostdlib))
addPlatformSearchDirs(*ctx, triple, baseTriple);
for (auto *arg : parsedArgs->filtered(OPT_u))
ctx->addInitialUndefinedSymbol(arg->getValue());
for (auto *arg : parsedArgs->filtered(OPT_defsym)) {
StringRef sym, target;
uint64_t addr;
if (parseDefsymAsAbsolute(arg->getValue(), sym, addr)) {
ctx->addInitialAbsoluteSymbol(sym, addr);
} else if (parseDefsymAsAlias(arg->getValue(), sym, target)) {
ctx->addAlias(sym, target);
} else {
diag << "invalid --defsym: " << arg->getValue() << "\n";
return false;
}
}
for (auto *arg : parsedArgs->filtered(OPT_z)) {
StringRef opt = arg->getValue();
if (opt == "muldefs") {
ctx->setAllowDuplicates(true);
} else if (opt.startswith("max-page-size")) {
// Parse -z max-page-size option.
// The default page size is considered the minimum page size the user
// can set, check the user input if its atleast the minimum page size
// and does not exceed the maximum page size allowed for the target.
uint64_t maxPageSize = 0;
// Error if the page size user set is less than the maximum page size
// and greather than the default page size and the user page size is a
// modulo of the default page size.
if ((!parseMaxPageSize(opt, maxPageSize)) ||
(maxPageSize < ctx->getPageSize()) ||
(maxPageSize % ctx->getPageSize())) {
diag << "invalid option: " << opt << "\n";
return false;
}
ctx->setMaxPageSize(maxPageSize);
} else {
diag << "warning: ignoring unknown argument for -z: " << opt << "\n";
}
}
for (auto *arg : parsedArgs->filtered(OPT_rpath)) {
SmallVector<StringRef, 2> rpaths;
StringRef(arg->getValue()).split(rpaths, ":");
for (auto path : rpaths)
ctx->addRpath(path);
}
for (auto *arg : parsedArgs->filtered(OPT_rpath_link)) {
SmallVector<StringRef, 2> rpaths;
StringRef(arg->getValue()).split(rpaths, ":");
for (auto path : rpaths)
ctx->addRpathLink(path);
}
// Support --wrap option.
for (auto *arg : parsedArgs->filtered(OPT_wrap))
ctx->addWrapForSymbol(arg->getValue());
// Register possible input file parsers.
ctx->registry().addSupportELFObjects(*ctx);
ctx->registry().addSupportArchives(ctx->logInputFiles());
ctx->registry().addSupportYamlFiles();
ctx->registry().addSupportNativeObjects();
if (ctx->allowLinkWithDynamicLibraries())
ctx->registry().addSupportELFDynamicSharedObjects(*ctx);
std::stack<int> groupStack;
int numfiles = 0;
bool asNeeded = false;
bool wholeArchive = false;
// Process files
for (auto arg : *parsedArgs) {
switch (arg->getOption().getID()) {
case OPT_no_whole_archive:
wholeArchive = false;
break;
case OPT_whole_archive:
wholeArchive = true;
break;
case OPT_as_needed:
asNeeded = true;
break;
case OPT_no_as_needed:
asNeeded = false;
break;
case OPT_start_group:
groupStack.push(numfiles);
break;
case OPT_end_group: {
if (groupStack.empty()) {
diag << "stray --end-group\n";
return false;
}
int startGroupPos = groupStack.top();
ctx->getNodes().push_back(
llvm::make_unique<GroupEnd>(numfiles - startGroupPos));
groupStack.pop();
break;
}
case OPT_INPUT:
case OPT_l:
case OPT_T: {
bool dashL = (arg->getOption().getID() == OPT_l);
StringRef path = arg->getValue();
ErrorOr<StringRef> pathOrErr = findFile(*ctx, path, dashL);
if (std::error_code ec = pathOrErr.getError()) {
auto file = llvm::make_unique<ErrorFile>(path, ec);
auto node = llvm::make_unique<FileNode>(std::move(file));
node->setAsNeeded(asNeeded);
ctx->getNodes().push_back(std::move(node));
break;
}
StringRef realpath = pathOrErr.get();
bool isScript =
(!path.endswith(".objtxt") && isLinkerScript(realpath, diag));
if (isScript) {
if (ctx->logInputFiles())
diag << path << "\n";
ErrorOr<std::unique_ptr<MemoryBuffer>> mb =
MemoryBuffer::getFileOrSTDIN(realpath);
if (std::error_code ec = mb.getError()) {
diag << "Cannot open " << path << ": " << ec.message() << "\n";
return false;
}
bool nostdlib = parsedArgs->hasArg(OPT_nostdlib);
std::error_code ec =
evalLinkerScript(*ctx, std::move(mb.get()), diag, nostdlib);
if (ec) {
diag << path << ": Error parsing linker script: "
<< ec.message() << "\n";
return false;
}
break;
}
std::vector<std::unique_ptr<File>> files
= loadFile(*ctx, realpath, wholeArchive);
for (std::unique_ptr<File> &file : files) {
if (ctx->logInputFiles())
diag << file->path() << "\n";
auto node = llvm::make_unique<FileNode>(std::move(file));
node->setAsNeeded(asNeeded);
ctx->getNodes().push_back(std::move(node));
}
numfiles += files.size();
break;
}
}
}
if (ctx->getNodes().empty()) {
diag << "No input files\n";
return false;
}
// Set default output file name if the output file was not specified.
if (ctx->outputPath().empty()) {
switch (ctx->outputFileType()) {
case LinkingContext::OutputFileType::YAML:
ctx->setOutputPath("-");
break;
case LinkingContext::OutputFileType::Native:
ctx->setOutputPath("a.native");
break;
default:
ctx->setOutputPath("a.out");
break;
}
}
// Validate the combination of options used.
if (!ctx->validate(diag))
return false;
// Perform linker script semantic actions
ctx->linkerScriptSema().perform();
context.swap(ctx);
return true;
}
/// Get the default target triple based on either the program name
/// (e.g. "x86-ibm-linux-lld") or the primary target llvm was configured for.
llvm::Triple GnuLdDriver::getDefaultTarget(const char *progName) {
SmallVector<StringRef, 4> components;
llvm::SplitString(llvm::sys::path::stem(progName), components, "-");
// If has enough parts to be start with a triple.
if (components.size() >= 4) {
llvm::Triple triple(components[0], components[1], components[2],
components[3]);
// If first component looks like an arch.
if (triple.getArch() != llvm::Triple::UnknownArch)
return triple;
}
// Fallback to use whatever default triple llvm was configured for.
return llvm::Triple(llvm::sys::getDefaultTargetTriple());
}

323
lib/Driver/GnuLdOptions.td Normal file
View file

@ -0,0 +1,323 @@
include "llvm/Option/OptParser.td"
//===----------------------------------------------------------------------===//
/// Utility Functions
//===----------------------------------------------------------------------===//
// Single and multiple dash options combined
multiclass smDash<string opt1, string opt2, string help> {
// Option
def "" : Separate<["-"], opt1>, HelpText<help>;
def opt1_eq : Joined<["-"], opt1#"=">,
Alias<!cast<Option>(opt1)>;
// Compatibility aliases
def opt2_dashdash : Separate<["--"], opt2>,
Alias<!cast<Option>(opt1)>;
def opt2_dashdash_eq : Joined<["--"], opt2#"=">,
Alias<!cast<Option>(opt1)>;
}
// Support -<option>,-<option>=
multiclass dashEq<string opt1, string opt2, string help> {
// Option
def "" : Separate<["-"], opt1>, HelpText<help>;
// Compatibility aliases
def opt2_eq : Joined<["-"], opt2#"=">,
Alias<!cast<Option>(opt1)>;
}
//===----------------------------------------------------------------------===//
/// LLVM and Target options
//===----------------------------------------------------------------------===//
def grp_llvmtarget : OptionGroup<"opts">,
HelpText<"LLVM and Target Options">;
def mllvm : Separate<["-"], "mllvm">,
HelpText<"Options to pass to LLVM">, Group<grp_llvmtarget>;
def target : Separate<["-"], "target">, MetaVarName<"<triple>">,
HelpText<"Target triple to link for">,
Group<grp_llvmtarget>;
//===----------------------------------------------------------------------===//
/// Output Kinds
//===----------------------------------------------------------------------===//
def grp_kind : OptionGroup<"outs">,
HelpText<"OUTPUT KIND">;
def relocatable : Flag<["-"], "r">,
HelpText<"Create relocatable object file">, Group<grp_kind>;
def static : Flag<["-"], "static">,
HelpText<"Create static executable">, Group<grp_kind>;
def dynamic : Flag<["-"], "dynamic">,
HelpText<"Create dynamic executable (default)">,Group<grp_kind>;
def shared : Flag<["-"], "shared">,
HelpText<"Create dynamic library">, Group<grp_kind>;
// output kinds - compatibility aliases
def Bstatic : Flag<["-"], "Bstatic">, Alias<static>;
def Bshareable : Flag<["-"], "Bshareable">, Alias<shared>;
//===----------------------------------------------------------------------===//
/// General Options
//===----------------------------------------------------------------------===//
def grp_general : OptionGroup<"opts">,
HelpText<"GENERAL OPTIONS">;
def output : Separate<["-"], "o">, MetaVarName<"<path>">,
HelpText<"Path to file to write output">,
Group<grp_general>;
def m : Separate<["-"], "m">, MetaVarName<"<emulation>">,
HelpText<"Select target emulation">,
Group<grp_general>;
def build_id : Flag<["--"], "build-id">,
HelpText<"Request creation of \".note.gnu.build-id\" ELF note section">,
Group<grp_general>;
def sysroot : Joined<["--"], "sysroot=">,
HelpText<"Set the system root">,
Group<grp_general>;
//===----------------------------------------------------------------------===//
/// Executable Options
//===----------------------------------------------------------------------===//
def grp_main : OptionGroup<"opts">,
HelpText<"EXECUTABLE OPTIONS">;
def L : Joined<["-"], "L">, MetaVarName<"<dir>">,
HelpText<"Directory to search for libraries">,
Group<grp_main>;
def l : Joined<["-"], "l">, MetaVarName<"<libName>">,
HelpText<"Root name of library to use">,
Group<grp_main>;
def noinhibit_exec : Flag<["--"], "noinhibit-exec">,
HelpText<"Retain the executable output file whenever"
" it is still usable">,
Group<grp_main>;
defm e : smDash<"e", "entry",
"Name of entry point symbol">,
Group<grp_main>;
defm init: dashEq<"init", "init",
"Specify an initializer function">,
Group<grp_main>;
defm fini: dashEq<"fini", "fini",
"Specify a finalizer function">,
Group<grp_main>;
def whole_archive: Flag<["--"], "whole-archive">,
HelpText<"Force load of all members in a static library">,
Group<grp_main>;
def no_whole_archive: Flag<["--"], "no-whole-archive">,
HelpText<"Restores the default behavior of loading archive members">,
Group<grp_main>;
def nostdlib : Flag<["-"], "nostdlib">,
HelpText<"Disable default search path for libraries">,
Group<grp_main>;
def image_base : Separate<["--"], "image-base">,
HelpText<"Set the base address">,
Group<grp_main>;
//===----------------------------------------------------------------------===//
/// Static Executable Options
//===----------------------------------------------------------------------===//
def grp_staticexec : OptionGroup<"opts">,
HelpText<"STATIC EXECUTABLE OPTIONS">;
def nmagic : Flag<["--"], "nmagic">,
HelpText<"Turn off page alignment of sections,"
" and disable linking against shared libraries">,
Group<grp_staticexec>;
def omagic : Flag<["--"], "omagic">,
HelpText<"Set the text and data sections to be readable and writable."
" Also, do not page-align the data segment, and"
" disable linking against shared libraries.">,
Group<grp_staticexec>;
def no_omagic : Flag<["--"], "no-omagic">,
HelpText<"This option negates most of the effects of the -N option."
"Disable linking with shared libraries">,
Group<grp_staticexec>;
// Compatible Aliases
def nmagic_alias : Flag<["-"], "n">,
Alias<nmagic>;
def omagic_alias : Flag<["-"], "N">,
Alias<omagic>;
//===----------------------------------------------------------------------===//
/// Dynamic Library/Executable Options
//===----------------------------------------------------------------------===//
def grp_dynlibexec : OptionGroup<"opts">,
HelpText<"DYNAMIC LIBRARY/EXECUTABLE OPTIONS">;
def dynamic_linker : Joined<["--"], "dynamic-linker=">,
HelpText<"Set the path to the dynamic linker">, Group<grp_dynlibexec>;
// Executable options - compatibility aliases
def dynamic_linker_alias : Separate<["-"], "dynamic-linker">,
Alias<dynamic_linker>;
defm rpath : dashEq<"rpath", "rpath",
"Add a directory to the runtime library search path">,
Group<grp_dynlibexec>;
def rpath_link : Separate<["-"], "rpath-link">,
HelpText<"Specifies the first set of directories to search">,
Group<grp_dynlibexec>;
def export_dynamic : Flag<["-", "--"], "export-dynamic">,
HelpText<"Add all symbols to the dynamic symbol table"
" when creating executables">,
Group<grp_main>;
def alias_export_dynamic: Flag<["-"], "E">,
Alias<export_dynamic>;
def no_export_dynamic : Flag<["--"], "no-export-dynamic">,
Group<grp_main>;
//===----------------------------------------------------------------------===//
/// Dynamic Library Options
//===----------------------------------------------------------------------===//
def grp_dynlib : OptionGroup<"opts">,
HelpText<"DYNAMIC LIBRARY OPTIONS">;
def soname : Joined<["-", "--"], "soname=">,
HelpText<"Set the internal DT_SONAME field to the specified name">,
Group<grp_dynlib>;
def soname_separate : Separate<["-", "--"], "soname">, Alias<soname>;
def soname_h : Separate<["-"], "h">, Alias<soname>;
//===----------------------------------------------------------------------===//
/// Resolver Options
//===----------------------------------------------------------------------===//
def grp_resolveropt : OptionGroup<"opts">,
HelpText<"SYMBOL RESOLUTION OPTIONS">;
defm u : smDash<"u", "undefined",
"Force symbol to be entered in the output file"
" as an undefined symbol">,
Group<grp_resolveropt>;
def start_group : Flag<["--"], "start-group">,
HelpText<"Start a group">,
Group<grp_resolveropt>;
def alias_start_group: Flag<["-"], "(">,
Alias<start_group>;
def end_group : Flag<["--"], "end-group">,
HelpText<"End a group">,
Group<grp_resolveropt>;
def alias_end_group: Flag<["-"], ")">,
Alias<end_group>;
def as_needed : Flag<["--"], "as-needed">,
HelpText<"This option affects ELF DT_NEEDED tags for "
"dynamic libraries mentioned on the command line">,
Group<grp_resolveropt>;
def no_as_needed : Flag<["--"], "no-as-needed">,
HelpText<"This option restores the default behavior"
" of adding DT_NEEDED entries">,
Group<grp_resolveropt>;
def no_allow_shlib_undefs : Flag<["--"], "no-allow-shlib-undefined">,
HelpText<"Do not allow undefined symbols from dynamic"
" library when creating executables">,
Group<grp_resolveropt>;
def allow_shlib_undefs : Flag<["-", "--"], "allow-shlib-undefined">,
HelpText<"Allow undefined symbols from dynamic"
" library when creating executables">,
Group<grp_resolveropt>;
def use_shlib_undefs: Flag<["--"], "use-shlib-undefines">,
HelpText<"Resolve undefined symbols from dynamic libraries">,
Group<grp_resolveropt>;
def allow_multiple_definition: Flag<["--"], "allow-multiple-definition">,
HelpText<"Allow multiple definitions">,
Group<grp_resolveropt>;
def defsym : Joined<["--"], "defsym=">,
HelpText<"Create a defined symbol">,
Group<grp_resolveropt>;
//===----------------------------------------------------------------------===//
/// Custom Options
//===----------------------------------------------------------------------===//
def grp_customopts : OptionGroup<"opts">,
HelpText<"CUSTOM OPTIONS">;
def rosegment: Flag<["--"], "rosegment">,
HelpText<"Put read-only non-executable sections in their own segment">,
Group<grp_customopts>;
def z : Separate<["-"], "z">,
HelpText<"Linker Option extensions">,
Group<grp_customopts>;
def no_align_segments: Flag<["--"], "no-align-segments">,
HelpText<"Don't align ELF segments(virtualaddress/fileoffset) to page boundaries">,
Group<grp_customopts>;
//===----------------------------------------------------------------------===//
/// Symbol options
//===----------------------------------------------------------------------===//
def grp_symbolopts : OptionGroup<"opts">,
HelpText<"SYMBOL OPTIONS">;
def demangle : Flag<["--"], "demangle">,
HelpText<"Demangle C++ symbols">,
Group<grp_symbolopts>;
def no_demangle : Flag<["--"], "no-demangle">,
HelpText<"Dont demangle C++ symbols">,
Group<grp_symbolopts>;
def strip_all : Flag<["--"], "strip-all">,
HelpText<"Omit all symbol informations from output">,
Group<grp_symbolopts>;
def alias_strip_all : Flag<["-"], "s">,
Alias<strip_all>;
defm wrap : smDash<"wrap", "wrap",
"Use a wrapper function for symbol. Any "
" undefined reference to symbol will be resolved to "
"\"__wrap_symbol\". Any undefined reference to \"__real_symbol\""
" will be resolved to symbol.">,
MetaVarName<"<symbol>">,
Group<grp_symbolopts>;
//===----------------------------------------------------------------------===//
/// Script Options
//===----------------------------------------------------------------------===//
def grp_scriptopts : OptionGroup<"opts">,
HelpText<"SCRIPT OPTIONS">;
defm T : smDash<"T", "script",
"Use the given linker script in place of the default script.">,
Group<grp_scriptopts>;
//===----------------------------------------------------------------------===//
/// Optimization Options
//===----------------------------------------------------------------------===//
def grp_opts : OptionGroup<"opts">,
HelpText<"OPTIMIZATIONS">;
def hash_style : Joined <["--"], "hash-style=">,
HelpText<"Set the type of linker's hash table(s)">,
Group<grp_opts>;
def merge_strings : Flag<["--"], "merge-strings">,
HelpText<"Merge common strings across mergeable sections">,
Group<grp_opts>;
def eh_frame_hdr : Flag<["--"], "eh-frame-hdr">,
HelpText<"Request creation of .eh_frame_hdr section and ELF "
" PT_GNU_EH_FRAME segment header">,
Group<grp_opts>;
//===----------------------------------------------------------------------===//
/// Tracing Options
//===----------------------------------------------------------------------===//
def grp_tracingopts : OptionGroup<"opts">,
HelpText<"TRACING OPTIONS">;
def t : Flag<["-"], "t">,
HelpText<"Print the names of the input files as ld processes them">,
Group<grp_tracingopts>;
def stats : Flag<["--"], "stats">,
HelpText<"Print time and memory usage stats">, Group<grp_tracingopts>;
//===----------------------------------------------------------------------===//
/// Extensions
//===----------------------------------------------------------------------===//
def grp_extns : OptionGroup<"opts">,
HelpText<"Extensions">;
def output_filetype: Separate<["--"], "output-filetype">,
HelpText<"Specify what type of output file that lld creates, YAML/Native">,
Group<grp_extns>;
def alias_output_filetype: Joined<["--"], "output-filetype=">,
Alias<output_filetype>;
//===----------------------------------------------------------------------===//
/// Ignored options
//===----------------------------------------------------------------------===//
def grp_ignored: OptionGroup<"ignored">,
HelpText<"GNU Options ignored for Compatibility ">;
def dashg : Flag<["-"], "g">,
HelpText<"Ignored.">,
Group<grp_ignored>;
def Qy : Flag<["-"], "Qy">,
HelpText<"Ignored for SVR4 Compatibility">,
Group<grp_ignored>;
def qmagic : Flag<["-"], "qmagic">,
HelpText<"Ignored for Linux Compatibility">,
Group<grp_ignored>;
//===----------------------------------------------------------------------===//
/// Help
//===----------------------------------------------------------------------===//
def help : Flag<["--"], "help">,
HelpText<"Display this help message">;

38
lib/Driver/Makefile Normal file
View file

@ -0,0 +1,38 @@
##===- lld/lib/Driver/Makefile ---------------------------*- Makefile -*-===##
#
# The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
##===----------------------------------------------------------------------===##
LLD_LEVEL := ../..
LIBRARYNAME := lldDriver
BUILT_SOURCES = CoreOptions.inc UniversalDriverOptions.inc DarwinLdOptions.inc \
GnuLdOptions.inc WinLinkOptions.inc
TABLEGEN_INC_FILES_COMMON = 1
include $(LLD_LEVEL)/Makefile
$(ObjDir)/CoreOptions.inc.tmp : CoreOptions.td $(LLVM_TBLGEN) $(ObjDir)/.dir
$(Echo) "Building LLD CoreOptions Option tables with tblgen"
$(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $<
$(ObjDir)/UniversalDriverOptions.inc.tmp : UniversalDriverOptions.td $(LLVM_TBLGEN) $(ObjDir)/.dir
$(Echo) "Building LLD Universal Driver Options tables with tblgen"
$(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $<
$(ObjDir)/DarwinLdOptions.inc.tmp : DarwinLdOptions.td $(LLVM_TBLGEN) $(ObjDir)/.dir
$(Echo) "Building LLD Darwin ld Option tables with tblgen"
$(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $<
$(ObjDir)/GnuLdOptions.inc.tmp : GnuLdOptions.td $(LLVM_TBLGEN) $(ObjDir)/.dir
$(Echo) "Building LLD Gnu ld Option tables with tblgen"
$(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $<
$(ObjDir)/WinLinkOptions.inc.tmp : WinLinkOptions.td $(LLVM_TBLGEN) $(ObjDir)/.dir
$(Echo) "Building LLD WinLinkOptions Option tables with tblgen"
$(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $<

101
lib/Driver/TODO.rst Normal file
View file

@ -0,0 +1,101 @@
GNU ld Driver
~~~~~~~~~~~~~
Missing Options
###############
* --audit
* -A,--architecture
* -b,--format
* -d,-dc,-dp
* -P,--depaudit
* --exclude-libs
* --exclude-modules-for-implib
* -E,--export-dynamic,--no-export-dynamic
* -EB (We probably shouldn't support this)
* -EL (We probably shouldn't support this)
* -f,--auxiliary
* -F,--filter
* -G,--gpsize
* -h
* -i
* --library
* -M
* --print-map
* -output
* -O
* -q,--emit-relocs
* --force-dynamic
* --relocatable
* -R,--just-symbols
* -s,--strip-all
* -S,--strip-debug
* --trace
* -dT,--default-script
* -Ur
* --unique
* -v,--version,-V
* -x,--discard-all
* -X,--discard-locals
* -y,--trace-symbol
* -z (keywords need to be implemented)
* --accept-unknown-input-arch,--no-accept-unknown-input-arch
* -Bdynamic,-dy,-call_shared
* -Bgroup
* -dn,-non_shared
* -Bsymbolic
* -Bsymbolic-functions
* --dynamic-list
* --dynamic-list-data
* --dynamic-list-cpp-new
* --dynamic-list-cpp-typeinfo
* --check-sections,--no-check-sections
* --copy-dt-needed-entries,--no-copy-dt-needed-entires
* --cref
* --no-define-common
* --defsym (only absolute value supported now)
* --demangle,--no-demangle
* -I
* --fatal-warnings,--no-fatal-warnings
* --force-exe-suffix
* --gc-sections,--no-gc-sections
* --print-gc-sections,--no-print-gc-sections
* --print-output-format
* --target-help
* -Map
* --no-keep-memory
* --no-undefined,-z defs
* --allow-shlib-undefined,--no-alow-shlib-undefined
* --no-undefined-version
* --default-symver
* --default-imported-symver
* --no-warn-mismatch
* --no-warn-search-mismatch
* --oformat
* -pie,--pic-executable
* --relax,--no-relax
* --retain-symbols-file
* --sort-common
* --sort-section={name,alignment}
* --split-by-file
* --split-by-reloc
* --stats
* --section-start
* -T{bss,data,text,{text,rodata,data}-segment}
* --unresolved-symbols
* -dll-verbose,--verbose
* --version-script
* --warn-common
* --warn-constructors
* --warn-multiple-gp
* --warn-once
* --warn-section-align
* --warn-shared-textrel
* --warn-alternate-em
* --warn-unresolved-symbols
* --error-unresolved-symbols
* --wrap
* --no-ld-generated-unwind-info
* --hash-size
* --reduce-memory-overheads
* --build-id

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