diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2015-03-24 21:31:36 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2015-03-24 21:31:36 +0000 |
commit | fb911942f1434f3d1750f83f25f5e42c80e60638 (patch) | |
tree | 1678c4a4f0182e4029a86d135aa4a1b7d09e3c41 |
Notes
1131 files changed, 106976 insertions, 0 deletions
diff --git a/.arcconfig b/.arcconfig new file mode 100644 index 0000000000000..787b339a9f202 --- /dev/null +++ b/.arcconfig @@ -0,0 +1,4 @@ +{ + "project_id" : "lld", + "conduit_uri" : "http://reviews.llvm.org/" +} diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000000..9b3aa8b7213b2 --- /dev/null +++ b/.clang-format @@ -0,0 +1 @@ +BasedOnStyle: LLVM diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000..0a288ee8ce966 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000000..30ef47a692d20 --- /dev/null +++ b/CMakeLists.txt @@ -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) diff --git a/LICENSE.TXT b/LICENSE.TXT new file mode 100644 index 0000000000000..bcb83b211422e --- /dev/null +++ b/LICENSE.TXT @@ -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> diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000000..e1b6a678fc23d --- /dev/null +++ b/Makefile @@ -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 diff --git a/README.md b/README.md new file mode 100644 index 0000000000000..dc05cdea0a12b --- /dev/null +++ b/README.md @@ -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. diff --git a/cmake/modules/FindVTune.cmake b/cmake/modules/FindVTune.cmake new file mode 100644 index 0000000000000..bd0cbe9a38cba --- /dev/null +++ b/cmake/modules/FindVTune.cmake @@ -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) diff --git a/docs/C++11.rst b/docs/C++11.rst new file mode 100644 index 0000000000000..0c4391e7b037e --- /dev/null +++ b/docs/C++11.rst @@ -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 diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt new file mode 100644 index 0000000000000..d4f3b058efb73 --- /dev/null +++ b/docs/CMakeLists.txt @@ -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() diff --git a/docs/Driver.rst b/docs/Driver.rst new file mode 100644 index 0000000000000..5f2d946d36bd9 --- /dev/null +++ b/docs/Driver.rst @@ -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`. diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000000000..4c147eb111376 --- /dev/null +++ b/docs/Makefile @@ -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." diff --git a/docs/README.txt b/docs/README.txt new file mode 100644 index 0000000000000..eb09a2d2b7ea9 --- /dev/null +++ b/docs/README.txt @@ -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 + diff --git a/docs/Readers.rst b/docs/Readers.rst new file mode 100644 index 0000000000000..e00406b8c4ced --- /dev/null +++ b/docs/Readers.rst @@ -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. + + + diff --git a/docs/_static/favicon.ico b/docs/_static/favicon.ico Binary files differnew file mode 100644 index 0000000000000..724ad6e12dd40 --- /dev/null +++ b/docs/_static/favicon.ico diff --git a/docs/_templates/indexsidebar.html b/docs/_templates/indexsidebar.html new file mode 100644 index 0000000000000..61968f22d5c5b --- /dev/null +++ b/docs/_templates/indexsidebar.html @@ -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> diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html new file mode 100644 index 0000000000000..519a24bce63a9 --- /dev/null +++ b/docs/_templates/layout.html @@ -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> | </li> +{% endblock %} diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000000000..99866e1bd1e15 --- /dev/null +++ b/docs/conf.py @@ -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 diff --git a/docs/design.rst b/docs/design.rst new file mode 100644 index 0000000000000..06d356527f587 --- /dev/null +++ b/docs/design.rst @@ -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. diff --git a/docs/development.rst b/docs/development.rst new file mode 100644 index 0000000000000..918e1778b801c --- /dev/null +++ b/docs/development.rst @@ -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 diff --git a/docs/getting_started.rst b/docs/getting_started.rst new file mode 100644 index 0000000000000..986a406c1cb72 --- /dev/null +++ b/docs/getting_started.rst @@ -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 diff --git a/docs/hello.png b/docs/hello.png Binary files differnew file mode 100644 index 0000000000000..70df111f1abd1 --- /dev/null +++ b/docs/hello.png diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000000000..7a87ad8d05830 --- /dev/null +++ b/docs/index.rst @@ -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 diff --git a/docs/llvm-theme/layout.html b/docs/llvm-theme/layout.html new file mode 100644 index 0000000000000..0cd0918eac2ac --- /dev/null +++ b/docs/llvm-theme/layout.html @@ -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 %} diff --git a/docs/llvm-theme/static/contents.png b/docs/llvm-theme/static/contents.png Binary files differnew file mode 100644 index 0000000000000..7fb82154a1748 --- /dev/null +++ b/docs/llvm-theme/static/contents.png diff --git a/docs/llvm-theme/static/llvm.css b/docs/llvm-theme/static/llvm.css new file mode 100644 index 0000000000000..32802bb6a2d00 --- /dev/null +++ b/docs/llvm-theme/static/llvm.css @@ -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; +} diff --git a/docs/llvm-theme/static/logo.png b/docs/llvm-theme/static/logo.png Binary files differnew file mode 100644 index 0000000000000..4fc899028dc6e --- /dev/null +++ b/docs/llvm-theme/static/logo.png diff --git a/docs/llvm-theme/static/navigation.png b/docs/llvm-theme/static/navigation.png Binary files differnew file mode 100644 index 0000000000000..1081dc1439fb9 --- /dev/null +++ b/docs/llvm-theme/static/navigation.png diff --git a/docs/llvm-theme/theme.conf b/docs/llvm-theme/theme.conf new file mode 100644 index 0000000000000..330fc92ffa189 --- /dev/null +++ b/docs/llvm-theme/theme.conf @@ -0,0 +1,4 @@ +[theme] +inherit = basic +stylesheet = llvm.css +pygments_style = friendly diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000000000..8471252d709f5 --- /dev/null +++ b/docs/make.bat @@ -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 diff --git a/docs/open_projects.rst b/docs/open_projects.rst new file mode 100644 index 0000000000000..eb146c8b75421 --- /dev/null +++ b/docs/open_projects.rst @@ -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:: diff --git a/docs/sphinx_intro.rst b/docs/sphinx_intro.rst new file mode 100644 index 0000000000000..6845bc812e787 --- /dev/null +++ b/docs/sphinx_intro.rst @@ -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. diff --git a/docs/windows_support.rst b/docs/windows_support.rst new file mode 100644 index 0000000000000..d9906a72ea1e5 --- /dev/null +++ b/docs/windows_support.rst @@ -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. diff --git a/include/Makefile b/include/Makefile new file mode 100644 index 0000000000000..d8903356d9fbb --- /dev/null +++ b/include/Makefile @@ -0,0 +1,4 @@ +LLD_LEVEL := .. +DIRS := lld + +include $(LLD_LEVEL)/Makefile diff --git a/include/lld/Config/Makefile b/include/lld/Config/Makefile new file mode 100644 index 0000000000000..e2139220e3df7 --- /dev/null +++ b/include/lld/Config/Makefile @@ -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" \ + $< > $@ diff --git a/include/lld/Config/Version.h b/include/lld/Config/Version.h new file mode 100644 index 0000000000000..41433c1175ef2 --- /dev/null +++ b/include/lld/Config/Version.h @@ -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 diff --git a/include/lld/Config/Version.inc.in b/include/lld/Config/Version.inc.in new file mode 100644 index 0000000000000..c893a56686c0b --- /dev/null +++ b/include/lld/Config/Version.inc.in @@ -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@" diff --git a/include/lld/Core/AbsoluteAtom.h b/include/lld/Core/AbsoluteAtom.h new file mode 100644 index 0000000000000..ed25297cea812 --- /dev/null +++ b/include/lld/Core/AbsoluteAtom.h @@ -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 diff --git a/include/lld/Core/Alias.h b/include/lld/Core/Alias.h new file mode 100644 index 0000000000000..610022525ecb3 --- /dev/null +++ b/include/lld/Core/Alias.h @@ -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 diff --git a/include/lld/Core/ArchiveLibraryFile.h b/include/lld/Core/ArchiveLibraryFile.h new file mode 100644 index 0000000000000..ff379d4f3ecf9 --- /dev/null +++ b/include/lld/Core/ArchiveLibraryFile.h @@ -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 diff --git a/include/lld/Core/Atom.h b/include/lld/Core/Atom.h new file mode 100644 index 0000000000000..27fdde022ba79 --- /dev/null +++ b/include/lld/Core/Atom.h @@ -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 diff --git a/include/lld/Core/DefinedAtom.h b/include/lld/Core/DefinedAtom.h new file mode 100644 index 0000000000000..86d880c659b4f --- /dev/null +++ b/include/lld/Core/DefinedAtom.h @@ -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 diff --git a/include/lld/Core/Error.h b/include/lld/Core/Error.h new file mode 100644 index 0000000000000..7caa25018f40b --- /dev/null +++ b/include/lld/Core/Error.h @@ -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 diff --git a/include/lld/Core/File.h b/include/lld/Core/File.h new file mode 100644 index 0000000000000..25b177ec879cb --- /dev/null +++ b/include/lld/Core/File.h @@ -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 diff --git a/include/lld/Core/Instrumentation.h b/include/lld/Core/Instrumentation.h new file mode 100644 index 0000000000000..162375905e17f --- /dev/null +++ b/include/lld/Core/Instrumentation.h @@ -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 diff --git a/include/lld/Core/LLVM.h b/include/lld/Core/LLVM.h new file mode 100644 index 0000000000000..1bc1173bd48bb --- /dev/null +++ b/include/lld/Core/LLVM.h @@ -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 diff --git a/include/lld/Core/LinkingContext.h b/include/lld/Core/LinkingContext.h new file mode 100644 index 0000000000000..81a3b4b4eb71a --- /dev/null +++ b/include/lld/Core/LinkingContext.h @@ -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 ®istry() const { return _registry; } + Registry ®istry() { 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 diff --git a/include/lld/Core/Node.h b/include/lld/Core/Node.h new file mode 100644 index 0000000000000..cd38fbd4a482a --- /dev/null +++ b/include/lld/Core/Node.h @@ -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 diff --git a/include/lld/Core/Parallel.h b/include/lld/Core/Parallel.h new file mode 100644 index 0000000000000..65176ac2b04dc --- /dev/null +++ b/include/lld/Core/Parallel.h @@ -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 diff --git a/include/lld/Core/Pass.h b/include/lld/Core/Pass.h new file mode 100644 index 0000000000000..7a9d2453f482f --- /dev/null +++ b/include/lld/Core/Pass.h @@ -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 diff --git a/include/lld/Core/PassManager.h b/include/lld/Core/PassManager.h new file mode 100644 index 0000000000000..65fc4d806ceb7 --- /dev/null +++ b/include/lld/Core/PassManager.h @@ -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 diff --git a/include/lld/Core/Reader.h b/include/lld/Core/Reader.h new file mode 100644 index 0000000000000..ac90c5a7e85c6 --- /dev/null +++ b/include/lld/Core/Reader.h @@ -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 diff --git a/include/lld/Core/Reference.h b/include/lld/Core/Reference.h new file mode 100644 index 0000000000000..7a804c31e1821 --- /dev/null +++ b/include/lld/Core/Reference.h @@ -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 diff --git a/include/lld/Core/Resolver.h b/include/lld/Core/Resolver.h new file mode 100644 index 0000000000000..e16c07b839fa4 --- /dev/null +++ b/include/lld/Core/Resolver.h @@ -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 diff --git a/include/lld/Core/STDExtras.h b/include/lld/Core/STDExtras.h new file mode 100644 index 0000000000000..4a6183891844f --- /dev/null +++ b/include/lld/Core/STDExtras.h @@ -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 diff --git a/include/lld/Core/SharedLibraryAtom.h b/include/lld/Core/SharedLibraryAtom.h new file mode 100644 index 0000000000000..1b0c37c411386 --- /dev/null +++ b/include/lld/Core/SharedLibraryAtom.h @@ -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 diff --git a/include/lld/Core/SharedLibraryFile.h b/include/lld/Core/SharedLibraryFile.h new file mode 100644 index 0000000000000..2f84624287d80 --- /dev/null +++ b/include/lld/Core/SharedLibraryFile.h @@ -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 diff --git a/include/lld/Core/Simple.h b/include/lld/Core/Simple.h new file mode 100644 index 0000000000000..71d0c07023014 --- /dev/null +++ b/include/lld/Core/Simple.h @@ -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 diff --git a/include/lld/Core/SymbolTable.h b/include/lld/Core/SymbolTable.h new file mode 100644 index 0000000000000..683ed65e36359 --- /dev/null +++ b/include/lld/Core/SymbolTable.h @@ -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 diff --git a/include/lld/Core/TODO.txt b/include/lld/Core/TODO.txt new file mode 100644 index 0000000000000..8888c763ef65b --- /dev/null +++ b/include/lld/Core/TODO.txt @@ -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 diff --git a/include/lld/Core/UndefinedAtom.h b/include/lld/Core/UndefinedAtom.h new file mode 100644 index 0000000000000..7a835a4ebaa89 --- /dev/null +++ b/include/lld/Core/UndefinedAtom.h @@ -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 diff --git a/include/lld/Core/Writer.h b/include/lld/Core/Writer.h new file mode 100644 index 0000000000000..94c75d8d019f6 --- /dev/null +++ b/include/lld/Core/Writer.h @@ -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 diff --git a/include/lld/Core/range.h b/include/lld/Core/range.h new file mode 100644 index 0000000000000..614c9672955c7 --- /dev/null +++ b/include/lld/Core/range.h @@ -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 diff --git a/include/lld/Driver/Driver.h b/include/lld/Driver/Driver.h new file mode 100644 index 0000000000000..300d2356d0507 --- /dev/null +++ b/include/lld/Driver/Driver.h @@ -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 diff --git a/include/lld/Driver/WinLinkModuleDef.h b/include/lld/Driver/WinLinkModuleDef.h new file mode 100644 index 0000000000000..68c9a4bfef706 --- /dev/null +++ b/include/lld/Driver/WinLinkModuleDef.h @@ -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 diff --git a/include/lld/Makefile b/include/lld/Makefile new file mode 100644 index 0000000000000..5bfb8910313e3 --- /dev/null +++ b/include/lld/Makefile @@ -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 diff --git a/include/lld/ReaderWriter/AtomLayout.h b/include/lld/ReaderWriter/AtomLayout.h new file mode 100644 index 0000000000000..ad4cd0607b88a --- /dev/null +++ b/include/lld/ReaderWriter/AtomLayout.h @@ -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 diff --git a/include/lld/ReaderWriter/CoreLinkingContext.h b/include/lld/ReaderWriter/CoreLinkingContext.h new file mode 100644 index 0000000000000..d597ca46ddc7c --- /dev/null +++ b/include/lld/ReaderWriter/CoreLinkingContext.h @@ -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 diff --git a/include/lld/ReaderWriter/ELFLinkingContext.h b/include/lld/ReaderWriter/ELFLinkingContext.h new file mode 100644 index 0000000000000..d1cd3d9f3d6b5 --- /dev/null +++ b/include/lld/ReaderWriter/ELFLinkingContext.h @@ -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 diff --git a/include/lld/ReaderWriter/ELFTargets.h b/include/lld/ReaderWriter/ELFTargets.h new file mode 100644 index 0000000000000..3d00339818e26 --- /dev/null +++ b/include/lld/ReaderWriter/ELFTargets.h @@ -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 diff --git a/include/lld/ReaderWriter/LinkerScript.h b/include/lld/ReaderWriter/LinkerScript.h new file mode 100644 index 0000000000000..ae8d18d830c62 --- /dev/null +++ b/include/lld/ReaderWriter/LinkerScript.h @@ -0,0 +1,1396 @@ +//===- ReaderWriter/LinkerScript.h ----------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Linker script parser. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_LINKER_SCRIPT_H +#define LLD_READER_WRITER_LINKER_SCRIPT_H + +#include "lld/Core/Error.h" +#include "lld/Core/LLVM.h" +#include "lld/Core/range.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/raw_ostream.h" +#include <memory> +#include <system_error> +#include <unordered_map> +#include <vector> + +namespace lld { +namespace script { +class Token { +public: + enum Kind { + unknown, + eof, + exclaim, + exclaimequal, + amp, + ampequal, + l_paren, + r_paren, + star, + starequal, + plus, + plusequal, + comma, + minus, + minusequal, + slash, + slashequal, + number, + colon, + semicolon, + less, + lessequal, + lessless, + lesslessequal, + equal, + equalequal, + greater, + greaterequal, + greatergreater, + greatergreaterequal, + question, + identifier, + libname, + kw_align, + kw_align_with_input, + kw_as_needed, + kw_at, + kw_discard, + kw_entry, + kw_exclude_file, + kw_extern, + kw_group, + kw_hidden, + kw_input, + kw_keep, + kw_length, + kw_memory, + kw_origin, + kw_provide, + kw_provide_hidden, + kw_only_if_ro, + kw_only_if_rw, + kw_output, + kw_output_arch, + kw_output_format, + kw_overlay, + kw_search_dir, + kw_sections, + kw_sort_by_alignment, + kw_sort_by_init_priority, + kw_sort_by_name, + kw_sort_none, + kw_subalign, + l_brace, + pipe, + pipeequal, + r_brace, + tilde + }; + + Token() : _kind(unknown) {} + Token(StringRef range, Kind kind) : _range(range), _kind(kind) {} + + void dump(raw_ostream &os) const; + + StringRef _range; + Kind _kind; +}; + +class Lexer { +public: + explicit Lexer(std::unique_ptr<MemoryBuffer> mb) : _buffer(mb->getBuffer()) { + _sourceManager.AddNewSourceBuffer(std::move(mb), llvm::SMLoc()); + } + + void lex(Token &tok); + + const llvm::SourceMgr &getSourceMgr() const { return _sourceManager; } + +private: + bool canStartNumber(char c) const; + bool canContinueNumber(char c) const; + bool canStartName(char c) const; + bool canContinueName(char c) const; + void skipWhitespace(); + + Token _current; + /// \brief The current buffer state. + StringRef _buffer; + // Lexer owns the input files. + llvm::SourceMgr _sourceManager; +}; + +/// All linker scripts commands derive from this class. High-level, sections and +/// output section commands are all subclasses of this class. +/// Examples: +/// +/// OUTPUT_FORMAT("elf64-x86-64") /* A linker script command */ +/// OUTPUT_ARCH(i386:x86-64) /* Another command */ +/// ENTRY(_start) /* Another command */ +/// +/// SECTIONS /* Another command */ +/// { +/// .interp : { /* A sections-command */ +/// *(.interp) /* An output-section-command */ +/// } +/// } +/// +class Command { +public: + enum class Kind { + Entry, + Extern, + Group, + Input, + InputSectionsCmd, + InputSectionName, + Memory, + Output, + OutputArch, + OutputFormat, + OutputSectionDescription, + Overlay, + SearchDir, + Sections, + SortedGroup, + SymbolAssignment, + }; + + Kind getKind() const { return _kind; } + inline llvm::BumpPtrAllocator &getAllocator() const; + + virtual void dump(raw_ostream &os) const = 0; + + virtual ~Command() {} + +protected: + Command(class Parser &ctx, Kind k) : _ctx(ctx), _kind(k) {} + +private: + Parser &_ctx; + Kind _kind; +}; + +class Output : public Command { +public: + Output(Parser &ctx, StringRef outputFileName) + : Command(ctx, Kind::Output), _outputFileName(outputFileName) {} + + static bool classof(const Command *c) { return c->getKind() == Kind::Output; } + + void dump(raw_ostream &os) const override { + os << "OUTPUT(" << _outputFileName << ")\n"; + } + + StringRef getOutputFileName() const { return _outputFileName; } + +private: + StringRef _outputFileName; +}; + +class OutputFormat : public Command { +public: + OutputFormat(Parser &ctx, const SmallVectorImpl<StringRef> &formats) + : Command(ctx, Kind::OutputFormat) { + size_t numFormats = formats.size(); + StringRef *formatsStart = getAllocator().Allocate<StringRef>(numFormats); + std::copy(std::begin(formats), std::end(formats), formatsStart); + _formats = llvm::makeArrayRef(formatsStart, numFormats); + } + + static bool classof(const Command *c) { + return c->getKind() == Kind::OutputFormat; + } + + void dump(raw_ostream &os) const override { + os << "OUTPUT_FORMAT("; + bool first = true; + for (StringRef format : _formats) { + if (!first) + os << ","; + first = false; + os << "\"" << format << "\""; + } + os << ")\n"; + } + + llvm::ArrayRef<StringRef> getFormats() { return _formats; } + +private: + llvm::ArrayRef<StringRef> _formats; +}; + +class OutputArch : public Command { +public: + OutputArch(Parser &ctx, StringRef arch) + : Command(ctx, Kind::OutputArch), _arch(arch) {} + + static bool classof(const Command *c) { + return c->getKind() == Kind::OutputArch; + } + + void dump(raw_ostream &os) const override { + os << "OUTPUT_ARCH(" << getArch() << ")\n"; + } + + StringRef getArch() const { return _arch; } + +private: + StringRef _arch; +}; + +struct Path { + StringRef _path; + bool _asNeeded; + bool _isDashlPrefix; + + Path() : _asNeeded(false), _isDashlPrefix(false) {} + Path(StringRef path, bool asNeeded = false, bool isLib = false) + : _path(path), _asNeeded(asNeeded), _isDashlPrefix(isLib) {} +}; + +template<Command::Kind K> +class PathList : public Command { +public: + PathList(Parser &ctx, StringRef name, const SmallVectorImpl<Path> &paths) + : Command(ctx, K), _name(name) { + size_t numPaths = paths.size(); + Path *pathsStart = getAllocator().template Allocate<Path>(numPaths); + std::copy(std::begin(paths), std::end(paths), pathsStart); + _paths = llvm::makeArrayRef(pathsStart, numPaths); + } + + static bool classof(const Command *c) { return c->getKind() == K; } + + void dump(raw_ostream &os) const override { + os << _name << "("; + bool first = true; + for (const Path &path : getPaths()) { + if (!first) + os << " "; + first = false; + if (path._asNeeded) + os << "AS_NEEDED("; + if (path._isDashlPrefix) + os << "-l"; + os << path._path; + if (path._asNeeded) + os << ")"; + } + os << ")\n"; + } + + llvm::ArrayRef<Path> getPaths() const { return _paths; } + +private: + StringRef _name; + llvm::ArrayRef<Path> _paths; +}; + +class Group : public PathList<Command::Kind::Group> { +public: + template <class RangeT> + Group(Parser &ctx, RangeT range) + : PathList(ctx, "GROUP", std::move(range)) {} +}; + +class Input : public PathList<Command::Kind::Input> { +public: + template <class RangeT> + Input(Parser &ctx, RangeT range) + : PathList(ctx, "INPUT", std::move(range)) {} +}; + +class Entry : public Command { +public: + Entry(Parser &ctx, StringRef entryName) + : Command(ctx, Kind::Entry), _entryName(entryName) {} + + static bool classof(const Command *c) { return c->getKind() == Kind::Entry; } + + void dump(raw_ostream &os) const override { + os << "ENTRY(" << _entryName << ")\n"; + } + + StringRef getEntryName() const { return _entryName; } + +private: + StringRef _entryName; +}; + +class SearchDir : public Command { +public: + SearchDir(Parser &ctx, StringRef searchPath) + : Command(ctx, Kind::SearchDir), _searchPath(searchPath) {} + + static bool classof(const Command *c) { + return c->getKind() == Kind::SearchDir; + } + + void dump(raw_ostream &os) const override { + os << "SEARCH_DIR(\"" << _searchPath << "\")\n"; + } + + StringRef getSearchPath() const { return _searchPath; } + +private: + StringRef _searchPath; +}; + +/// Superclass for expression nodes. Linker scripts accept C-like expressions in +/// many places, such as when defining the value of a symbol or the address of +/// an output section. +/// Example: +/// +/// SECTIONS { +/// my_symbol = 1 + 1 * 2; +/// | | ^~~~> Constant : Expression +/// | | ^~~~> Constant : Expression +/// | | ^~~~> BinOp : Expression +/// ^~~~> Constant : Expression +/// ^~~~> BinOp : Expression (the top-level Expression node) +/// } +/// +class Expression { +public: + // The symbol table does not need to own its string keys and the use of StringMap + // here is an overkill. + typedef llvm::StringMap<int64_t, llvm::BumpPtrAllocator> SymbolTableTy; + + enum class Kind { Constant, Symbol, FunctionCall, Unary, BinOp, + TernaryConditional }; + Kind getKind() const { return _kind; } + inline llvm::BumpPtrAllocator &getAllocator() const; + virtual void dump(raw_ostream &os) const = 0; + virtual ErrorOr<int64_t> evalExpr(SymbolTableTy &symbolTable) const = 0; + virtual ~Expression() {} + +protected: + Expression(class Parser &ctx, Kind k) : _ctx(ctx), _kind(k) {} + +private: + Parser &_ctx; + Kind _kind; +}; + +/// A constant value is stored as unsigned because it represents absolute +/// values. We represent negative numbers by composing the unary '-' operator +/// with a constant. +class Constant : public Expression { +public: + Constant(Parser &ctx, uint64_t num) + : Expression(ctx, Kind::Constant), _num(num) {} + void dump(raw_ostream &os) const override; + + static bool classof(const Expression *c) { + return c->getKind() == Kind::Constant; + } + + ErrorOr<int64_t> evalExpr(SymbolTableTy &symbolTable) const override; + +private: + uint64_t _num; +}; + +class Symbol : public Expression { +public: + Symbol(Parser &ctx, StringRef name) + : Expression(ctx, Kind::Symbol), _name(name) {} + void dump(raw_ostream &os) const override; + + static bool classof(const Expression *c) { + return c->getKind() == Kind::Symbol; + } + + ErrorOr<int64_t> evalExpr(SymbolTableTy &symbolTable) const override; + +private: + StringRef _name; +}; + +class FunctionCall : public Expression { +public: + FunctionCall(Parser &ctx, StringRef name, + const SmallVectorImpl<const Expression *> &args) + : Expression(ctx, Kind::FunctionCall), _name(name) { + size_t numArgs = args.size(); + const Expression **argsStart = + getAllocator().Allocate<const Expression *>(numArgs); + std::copy(std::begin(args), std::end(args), argsStart); + _args = llvm::makeArrayRef(argsStart, numArgs); + } + + void dump(raw_ostream &os) const override; + + static bool classof(const Expression *c) { + return c->getKind() == Kind::FunctionCall; + } + + ErrorOr<int64_t> evalExpr(SymbolTableTy &symbolTable) const override; + +private: + StringRef _name; + llvm::ArrayRef<const Expression *> _args; +}; + +class Unary : public Expression { +public: + enum Operation { + Minus, + Not + }; + + Unary(Parser &ctx, Operation op, const Expression *child) + : Expression(ctx, Kind::Unary), _op(op), _child(child) {} + void dump(raw_ostream &os) const override; + + static bool classof(const Expression *c) { + return c->getKind() == Kind::Unary; + } + + ErrorOr<int64_t> evalExpr(SymbolTableTy &symbolTable) const override; + +private: + Operation _op; + const Expression *_child; +}; + +class BinOp : public Expression { +public: + enum Operation { + And, + CompareDifferent, + CompareEqual, + CompareGreater, + CompareGreaterEqual, + CompareLess, + CompareLessEqual, + Div, + Mul, + Or, + Shl, + Shr, + Sub, + Sum + }; + + BinOp(Parser &ctx, const Expression *lhs, Operation op, const Expression *rhs) + : Expression(ctx, Kind::BinOp), _op(op), _lhs(lhs), _rhs(rhs) {} + + void dump(raw_ostream &os) const override; + + static bool classof(const Expression *c) { + return c->getKind() == Kind::BinOp; + } + + ErrorOr<int64_t> evalExpr(SymbolTableTy &symbolTable) const override; + +private: + Operation _op; + const Expression *_lhs; + const Expression *_rhs; +}; + +/// Operands of the ternary operator can be any expression, similar to the other +/// operations, including another ternary operator. To disambiguate the parse +/// tree, note that ternary conditionals have precedence 13 and, different from +/// other operators, associates right-to-left. For example: +/// +/// i = i > 3 ? i < 5 ? 1 : 2 : 0; +/// +/// will have the following parse tree: +/// +/// i = ((i > 3) ? ((i < 5) ? 1 : 2) : 0); +/// +/// The '>' binds tigher because it has precedence 6. When faced with two "?" +/// ternary operators back-to-back, the parser prioritized the rightmost one. +/// +class TernaryConditional : public Expression { +public: + TernaryConditional(Parser &ctx, const Expression *conditional, + const Expression *trueExpr, const Expression *falseExpr) + : Expression(ctx, Kind::TernaryConditional), _conditional(conditional), + _trueExpr(trueExpr), _falseExpr(falseExpr) {} + + void dump(raw_ostream &os) const override; + + static bool classof(const Expression *c) { + return c->getKind() == Kind::TernaryConditional; + } + + ErrorOr<int64_t> evalExpr(SymbolTableTy &symbolTable) const override; + +private: + const Expression *_conditional; + const Expression *_trueExpr; + const Expression *_falseExpr; +}; + +/// Symbol assignments of the form "symbolname = <expression>" may occur either +/// as sections-commands or as output-section-commands. +/// Example: +/// +/// SECTIONS { +/// mysymbol = . /* SymbolAssignment as a sections-command */ +/// .data : { +/// othersymbol = . /* SymbolAssignment as an output-section-command */ +/// } +///} +/// +class SymbolAssignment : public Command { +public: + enum AssignmentKind { Simple, Sum, Sub, Mul, Div, Shl, Shr, And, Or }; + enum AssignmentVisibility { Default, Hidden, Provide, ProvideHidden }; + + SymbolAssignment(Parser &ctx, StringRef name, const Expression *expr, + AssignmentKind kind, AssignmentVisibility visibility) + : Command(ctx, Kind::SymbolAssignment), _expression(expr), _symbol(name), + _assignmentKind(Simple), _assignmentVisibility(visibility) {} + + static bool classof(const Command *c) { + return c->getKind() == Kind::SymbolAssignment; + } + + void dump(raw_ostream &os) const override; + const Expression *expr() const { return _expression; } + StringRef symbol() const { return _symbol; } + AssignmentKind assignmentKind() const { return _assignmentKind; } + AssignmentVisibility assignmentVisibility() const { + return _assignmentVisibility; + } + +private: + const Expression *_expression; + StringRef _symbol; + AssignmentKind _assignmentKind; + AssignmentVisibility _assignmentVisibility; +}; + +/// Encodes how to sort file names or section names that are expanded from +/// wildcard operators. This typically occurs in constructs such as +/// SECTIONS { .data : SORT_BY_NAME(*)(*) }}, where the order of the expanded +/// names is important to determine which sections go first. +enum class WildcardSortMode { + NA, + ByAlignment, + ByAlignmentAndName, + ByInitPriority, + ByName, + ByNameAndAlignment, + None +}; + +/// Represents either a single input section name or a group of sorted input +/// section names. They specify which sections to map to a given output section. +/// Example: +/// +/// SECTIONS { +/// .x: { *(.text) } +/// /* ^~~~^ InputSectionName : InputSection */ +/// .y: { *(SORT(.text*)) } +/// /* ^~~~~~~~~~~^ InputSectionSortedGroup : InputSection */ +/// } +class InputSection : public Command { +public: + static bool classof(const Command *c) { + return c->getKind() == Kind::InputSectionName || + c->getKind() == Kind::SortedGroup; + } + +protected: + InputSection(Parser &ctx, Kind k) : Command(ctx, k) {} +}; + +class InputSectionName : public InputSection { +public: + InputSectionName(Parser &ctx, StringRef name, bool excludeFile) + : InputSection(ctx, Kind::InputSectionName), _name(name), + _excludeFile(excludeFile) {} + + void dump(raw_ostream &os) const override; + + static bool classof(const Command *c) { + return c->getKind() == Kind::InputSectionName; + } + bool hasExcludeFile() const { return _excludeFile; } + StringRef name() const { return _name; } + +private: + StringRef _name; + bool _excludeFile; +}; + +class InputSectionSortedGroup : public InputSection { +public: + typedef llvm::ArrayRef<const InputSection *>::const_iterator const_iterator; + + InputSectionSortedGroup(Parser &ctx, WildcardSortMode sort, + const SmallVectorImpl<const InputSection *> §ions) + : InputSection(ctx, Kind::SortedGroup), _sortMode(sort) { + size_t numSections = sections.size(); + const InputSection **sectionsStart = + getAllocator().Allocate<const InputSection *>(numSections); + std::copy(std::begin(sections), std::end(sections), sectionsStart); + _sections = llvm::makeArrayRef(sectionsStart, numSections); + } + + void dump(raw_ostream &os) const override; + WildcardSortMode sortMode() const { return _sortMode; } + + static bool classof(const Command *c) { + return c->getKind() == Kind::SortedGroup; + } + + const_iterator begin() const { return _sections.begin(); } + const_iterator end() const { return _sections.end(); } + +private: + WildcardSortMode _sortMode; + llvm::ArrayRef<const InputSection *> _sections; +}; + +/// An output-section-command that maps a series of sections inside a given +/// file-archive pair to an output section. +/// Example: +/// +/// SECTIONS { +/// .x: { *(.text) } +/// /* ^~~~~~~^ InputSectionsCmd */ +/// .y: { w:z(SORT(.text*)) } +/// /* ^~~~~~~~~~~~~~~~^ InputSectionsCmd */ +/// } +class InputSectionsCmd : public Command { +public: + typedef llvm::ArrayRef<const InputSection *>::const_iterator const_iterator; + typedef std::vector<const InputSection *> VectorTy; + + InputSectionsCmd(Parser &ctx, StringRef memberName, StringRef archiveName, + bool keep, WildcardSortMode fileSortMode, + WildcardSortMode archiveSortMode, + const SmallVectorImpl<const InputSection *> §ions) + : Command(ctx, Kind::InputSectionsCmd), _memberName(memberName), + _archiveName(archiveName), _keep(keep), _fileSortMode(fileSortMode), + _archiveSortMode(archiveSortMode) { + size_t numSections = sections.size(); + const InputSection **sectionsStart = + getAllocator().Allocate<const InputSection *>(numSections); + std::copy(std::begin(sections), std::end(sections), sectionsStart); + _sections = llvm::makeArrayRef(sectionsStart, numSections); + } + + void dump(raw_ostream &os) const override; + + static bool classof(const Command *c) { + return c->getKind() == Kind::InputSectionsCmd; + } + + StringRef memberName() const { return _memberName; } + StringRef archiveName() const { return _archiveName; } + const_iterator begin() const { return _sections.begin(); } + const_iterator end() const { return _sections.end(); } + WildcardSortMode archiveSortMode() const { return _archiveSortMode; } + WildcardSortMode fileSortMode() const { return _fileSortMode; } + +private: + StringRef _memberName; + StringRef _archiveName; + bool _keep; + WildcardSortMode _fileSortMode; + WildcardSortMode _archiveSortMode; + llvm::ArrayRef<const InputSection *> _sections; +}; + +/// A sections-command to specify which input sections and symbols compose a +/// given output section. +/// Example: +/// +/// SECTIONS { +/// .x: { *(.text) ; symbol = .; } +/// /*^~~~~~~~~~~~~~~~~~~~~~~~~~~~~^ OutputSectionDescription */ +/// .y: { w:z(SORT(.text*)) } +/// /*^~~~~~~~~~~~~~~~~~~~~~~~^ OutputSectionDescription */ +/// .a 0x10000 : ONLY_IF_RW { *(.data*) ; *:libc.a(SORT(*)); } +/// /*^~~~~~~~~~~~~ OutputSectionDescription ~~~~~~~~~~~~~~~~~^ */ +/// } +class OutputSectionDescription : public Command { +public: + enum Constraint { C_None, C_OnlyIfRO, C_OnlyIfRW }; + + typedef llvm::ArrayRef<const Command *>::const_iterator const_iterator; + + OutputSectionDescription( + Parser &ctx, StringRef sectionName, const Expression *address, + const Expression *align, const Expression *subAlign, const Expression *at, + const Expression *fillExpr, StringRef fillStream, bool alignWithInput, + bool discard, Constraint constraint, + const SmallVectorImpl<const Command *> &outputSectionCommands) + : Command(ctx, Kind::OutputSectionDescription), _sectionName(sectionName), + _address(address), _align(align), _subAlign(subAlign), _at(at), + _fillExpr(fillExpr), _fillStream(fillStream), + _alignWithInput(alignWithInput), _discard(discard), + _constraint(constraint) { + size_t numCommands = outputSectionCommands.size(); + const Command **commandsStart = + getAllocator().Allocate<const Command *>(numCommands); + std::copy(std::begin(outputSectionCommands), + std::end(outputSectionCommands), commandsStart); + _outputSectionCommands = llvm::makeArrayRef(commandsStart, numCommands); + } + + static bool classof(const Command *c) { + return c->getKind() == Kind::OutputSectionDescription; + } + + void dump(raw_ostream &os) const override; + + const_iterator begin() const { return _outputSectionCommands.begin(); } + const_iterator end() const { return _outputSectionCommands.end(); } + StringRef name() const { return _sectionName; } + +private: + StringRef _sectionName; + const Expression *_address; + const Expression *_align; + const Expression *_subAlign; + const Expression *_at; + const Expression *_fillExpr; + StringRef _fillStream; + bool _alignWithInput; + bool _discard; + Constraint _constraint; + llvm::ArrayRef<const Command *> _outputSectionCommands; +}; + +/// Represents an Overlay structure as documented in +/// https://sourceware.org/binutils/docs/ld/Overlay-Description.html#Overlay-Description +class Overlay : public Command { +public: + Overlay(Parser &ctx) : Command(ctx, Kind::Overlay) {} + + static bool classof(const Command *c) { + return c->getKind() == Kind::Overlay; + } + + void dump(raw_ostream &os) const override { os << "Overlay description\n"; } +}; + +/// Represents all the contents of the SECTIONS {} construct. +class Sections : public Command { +public: + typedef llvm::ArrayRef<const Command *>::const_iterator const_iterator; + + Sections(Parser &ctx, + const SmallVectorImpl<const Command *> §ionsCommands) + : Command(ctx, Kind::Sections) { + size_t numCommands = sectionsCommands.size(); + const Command **commandsStart = + getAllocator().Allocate<const Command *>(numCommands); + std::copy(std::begin(sectionsCommands), std::end(sectionsCommands), + commandsStart); + _sectionsCommands = llvm::makeArrayRef(commandsStart, numCommands); + } + + static bool classof(const Command *c) { + return c->getKind() == Kind::Sections; + } + + void dump(raw_ostream &os) const override; + const_iterator begin() const { return _sectionsCommands.begin(); } + const_iterator end() const { return _sectionsCommands.end(); } + +private: + llvm::ArrayRef<const Command *> _sectionsCommands; +}; + +/// Represents a single memory block definition in a MEMORY {} command. +class MemoryBlock { +public: + MemoryBlock(StringRef name, StringRef attr, + const Expression *origin, const Expression *length) + : _name(name), _attr(attr), _origin(origin), _length(length) {} + + void dump(raw_ostream &os) const; + +private: + StringRef _name; + StringRef _attr; + const Expression *_origin; + const Expression *_length; +}; + +/// Represents all the contents of the MEMORY {} command. +class Memory : public Command { +public: + Memory(Parser &ctx, + const SmallVectorImpl<const MemoryBlock *> &blocks) + : Command(ctx, Kind::Memory) { + size_t numBlocks = blocks.size(); + const MemoryBlock **blocksStart = + getAllocator().Allocate<const MemoryBlock *>(numBlocks); + std::copy(std::begin(blocks), std::end(blocks), blocksStart); + _blocks = llvm::makeArrayRef(blocksStart, numBlocks); + } + + static bool classof(const Command *c) { + return c->getKind() == Kind::Memory; + } + + void dump(raw_ostream &os) const override; + +private: + llvm::ArrayRef<const MemoryBlock *> _blocks; +}; + +/// Represents an extern command. +class Extern : public Command { +public: + typedef llvm::ArrayRef<StringRef>::const_iterator const_iterator; + + Extern(Parser &ctx, + const SmallVectorImpl<StringRef> &symbols) + : Command(ctx, Kind::Extern) { + size_t numSymbols = symbols.size(); + StringRef *symbolsStart = + getAllocator().Allocate<StringRef>(numSymbols); + std::copy(std::begin(symbols), std::end(symbols), symbolsStart); + _symbols = llvm::makeArrayRef(symbolsStart, numSymbols); + } + + static bool classof(const Command *c) { + return c->getKind() == Kind::Extern; + } + + void dump(raw_ostream &os) const override; + const_iterator begin() const { return _symbols.begin(); } + const_iterator end() const { return _symbols.end(); } + +private: + llvm::ArrayRef<StringRef> _symbols; +}; + +/// Stores the parse tree of a linker script. +class LinkerScript { +public: + void dump(raw_ostream &os) const { + for (const Command *c : _commands) { + c->dump(os); + if (isa<SymbolAssignment>(c)) + os << "\n"; + } + } + + std::vector<const Command *> _commands; +}; + +/// Recognizes syntactic constructs of a linker script using a predictive +/// parser/recursive descent implementation. +/// +/// Based on the linker script documentation available at +/// https://sourceware.org/binutils/docs/ld/Scripts.html +class Parser { +public: + explicit Parser(std::unique_ptr<MemoryBuffer> mb) + : _lex(std::move(mb)), _peekAvailable(false) {} + + /// Let's not allow copying of Parser class because it would be expensive + /// to update all the AST pointers to a new buffer. + Parser(const Parser &instance) = delete; + + /// Lex and parse the current memory buffer to create a linker script AST. + std::error_code parse(); + + /// Returns a reference to the top level node of the linker script AST. + LinkerScript *get() { return &_script; } + + /// Returns a reference to the underlying allocator. + llvm::BumpPtrAllocator &getAllocator() { return _alloc; } + +private: + /// Advances to the next token, either asking the Lexer to lex the next token + /// or obtaining it from the look ahead buffer. + void consumeToken() { + // First check if the look ahead buffer cached the next token + if (_peekAvailable) { + _tok = _bufferedToken; + _peekAvailable = false; + return; + } + _lex.lex(_tok); + } + + /// Returns the token that succeeds the current one without consuming the + /// current token. This operation will lex an additional token and store it in + /// a private buffer. + const Token &peek() { + if (_peekAvailable) + return _bufferedToken; + + _lex.lex(_bufferedToken); + _peekAvailable = true; + return _bufferedToken; + } + + void error(const Token &tok, Twine msg) { + _lex.getSourceMgr().PrintMessage( + llvm::SMLoc::getFromPointer(tok._range.data()), + llvm::SourceMgr::DK_Error, msg); + } + + bool expectAndConsume(Token::Kind kind, Twine msg) { + if (_tok._kind != kind) { + error(_tok, msg); + return false; + } + consumeToken(); + return true; + } + + bool isNextToken(Token::Kind kind) { return (_tok._kind == kind); } + + // Recursive descent parsing member functions + // All of these functions consumes tokens and return an AST object, + // represented by the Command superclass. However, note that not all AST + // objects derive from Command. For nodes of C-like expressions, used in + // linker scripts, the superclass is Expression. For nodes that represent + // input sections that map to an output section, the superclass is + // InputSection. + // + // Example mapping common constructs to AST nodes: + // + // SECTIONS { /* Parsed to Sections class */ + // my_symbol = 1 + 1; /* Parsed to SymbolAssignment class */ + // /* ^~~> Parsed to Expression class */ + // .data : { *(.data) } /* Parsed to OutputSectionDescription class */ + // /* ^~~> Parsed to InputSectionName class */ + // /* ^~~~~> Parsed to InputSectionsCmd class */ + // } + + // ==== Expression parsing member functions ==== + + /// Parse "identifier(param [, param]...)" + /// + /// Example: + /// + /// SECTIONS { + /// my_symbol = 0x1000 | ALIGN(other_symbol); + /// /* ^~~~> parseFunctionCall() + /// } + const Expression *parseFunctionCall(); + + /// Ensures that the current token is an expression operand. If it is not, + /// issues an error to the user and returns false. + bool expectExprOperand(); + + /// Parse operands of an expression, such as function calls, identifiers, + /// literal numbers or unary operators. + /// + /// Example: + /// + /// SECTIONS { + /// my_symbol = 0x1000 | ALIGN(other_symbol); + /// ^~~~> parseExprTerminal() + /// } + const Expression *parseExprOperand(); + + // As a reference to the precedence of C operators, consult + // http://en.cppreference.com/w/c/language/operator_precedence + + /// Parse either a single expression operand and returns or parse an entire + /// expression if its top-level node has a lower or equal precedence than the + /// indicated. + const Expression *parseExpression(unsigned precedence = 13); + + /// Parse an operator and its rhs operand, assuming that the lhs was already + /// consumed. Keep parsing subsequent operator-operand pairs that do not + /// exceed highestPrecedence. + /// * lhs points to the left-hand-side operand of this operator + /// * maxPrecedence has the maximum operator precedence level that this parse + /// function is allowed to consume. + const Expression *parseOperatorOperandLoop(const Expression *lhs, + unsigned maxPrecedence); + + /// Parse ternary conditionals such as "(condition)? true: false;". This + /// operator has precedence level 13 and associates right-to-left. + const Expression *parseTernaryCondOp(const Expression *lhs); + + // ==== High-level commands parsing ==== + + /// Parse the OUTPUT linker script command. + /// Example: + /// OUTPUT(/path/to/file) + /// ^~~~> parseOutput() + /// + Output *parseOutput(); + + /// Parse the OUTPUT_FORMAT linker script command. + /// Example: + /// + /// OUTPUT_FORMAT(elf64-x86-64,elf64-x86-64,elf64-x86-64) + /// ^~~~> parseOutputFormat() + /// + OutputFormat *parseOutputFormat(); + + /// Parse the OUTPUT_ARCH linker script command. + /// Example: + /// + /// OUTPUT_ARCH(i386:x86-64) + /// ^~~~> parseOutputArch() + /// + OutputArch *parseOutputArch(); + + /// Parse the INPUT or GROUP linker script command. + /// Example: + /// + /// GROUP ( /lib/x86_64-linux-gnu/libc.so.6 + /// /usr/lib/x86_64-linux-gnu/libc_nonshared.a + /// AS_NEEDED ( /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 ) + /// -lm -l:libgcc.a ) + /// + template<class T> T *parsePathList(); + bool parseAsNeeded(SmallVectorImpl<Path> &paths); + + /// Parse the ENTRY linker script command. + /// Example: + /// + /// ENTRY(init) + /// ^~~~> parseEntry() + /// + Entry *parseEntry(); + + /// Parse the SEARCH_DIR linker script command. + /// Example: + /// + /// SEARCH_DIR("/usr/x86_64-linux-gnu/lib64"); + /// ^~~~> parseSearchDir() + /// + SearchDir *parseSearchDir(); + + /// Parse "symbol = expression" commands that live inside the + /// SECTIONS directive. + /// Example: + /// + /// SECTIONS { + /// my_symbol = 1 + 1; + /// ^~~~> parseExpression() + /// ^~~~ parseSymbolAssignment() + /// } + /// + const SymbolAssignment *parseSymbolAssignment(); + + /// Parse "EXCLUDE_FILE" used inside the listing of input section names. + /// Example: + /// + /// SECTIONS { + /// .data : { *(EXCLUDE_FILE (*crtend.o *otherfile.o) .ctors) } + /// ^~~~> parseExcludeFile() + /// } + /// + ErrorOr<InputSectionsCmd::VectorTy> parseExcludeFile(); + + /// Helper to parse SORT_BY_NAME(, SORT_BY_ALIGNMENT( and SORT_NONE(, + /// possibly nested. Returns the number of Token::r_paren tokens that need + /// to be consumed, while sortMode is updated with the parsed sort + /// criteria. + /// Example: + /// + /// SORT_BY_NAME(SORT_BY_ALIGNMENT(*)) + /// ^~~~ parseSortDirectives() ~~^ + /// Returns 2, finishes with sortMode = WildcardSortMode::ByNameAndAlignment + /// + int parseSortDirectives(WildcardSortMode &sortMode); + + /// Parse a group of input section names that are sorted via SORT* directives. + /// Example: + /// SORT_BY_NAME(SORT_BY_ALIGNMENT(*data *bss)) + const InputSection *parseSortedInputSections(); + + /// Parse input section description statements. + /// Example: + /// + /// SECTIONS { + /// .mysection : crt.o(.data* .bss SORT_BY_NAME(name*)) + /// ^~~~ parseInputSectionsCmd() + /// } + const InputSectionsCmd *parseInputSectionsCmd(); + + /// Parse output section description statements. + /// Example: + /// + /// SECTIONS { + /// .data : { crt.o(.data* .bss SORT_BY_NAME(name*)) } + /// ^~~~ parseOutputSectionDescription() + /// } + const OutputSectionDescription *parseOutputSectionDescription(); + + /// Stub for parsing overlay commands. Currently unimplemented. + const Overlay *parseOverlay(); + + /// Parse the SECTIONS linker script command. + /// Example: + /// + /// SECTIONS { + /// ^~~~ parseSections() + /// . = 0x100000; + /// .data : { *(.data) } + /// } + /// + Sections *parseSections(); + + /// Parse the MEMORY linker script command. + /// Example: + /// + /// MEMORY { + /// ^~~~ parseMemory() + /// ram (rwx) : ORIGIN = 0x20000000, LENGTH = 96K + /// rom (rx) : ORIGIN = 0x0, LENGTH = 256K + /// } + /// + Memory *parseMemory(); + + /// Parse the EXTERN linker script command. + /// Example: + /// + /// EXTERN(symbol symbol ...) + /// ^~~~> parseExtern() + /// + Extern *parseExtern(); + +private: + // Owns the entire linker script AST nodes + llvm::BumpPtrAllocator _alloc; + + // The top-level/entry-point linker script AST node + LinkerScript _script; + + Lexer _lex; + + // Current token being analyzed + Token _tok; + + // Annotate whether we buffered the next token to allow peeking + bool _peekAvailable; + Token _bufferedToken; +}; + +/// script::Sema traverses all parsed linker script structures and populate +/// internal data structures to be able to answer the following questions: +/// +/// * According to the linker script, which input section goes first in the +/// output file layout, input section A or input section B? +/// +/// * What is the name of the output section that input section A should be +/// mapped to? +/// +/// * Which linker script expressions should be calculated before emitting +/// a given section? +/// +/// * How to evaluate a given linker script expression? +/// +class Sema { +public: + /// From the linker script point of view, this class represents the minimum + /// set of information to uniquely identify an input section. + struct SectionKey { + StringRef archivePath; + StringRef memberPath; + StringRef sectionName; + }; + + Sema(); + + /// We can parse several linker scripts via command line whose ASTs are stored + /// here via addLinkerScript(). + void addLinkerScript(std::unique_ptr<Parser> script) { + _scripts.push_back(std::move(script)); + } + + const std::vector<std::unique_ptr<Parser>> &getLinkerScripts() { + return _scripts; + } + + /// Prepare our data structures according to the linker scripts currently in + /// our control (control given via addLinkerScript()). Called once all linker + /// scripts have been parsed. + void perform(); + + /// Answer if we have layout commands (section mapping rules). If we don't, + /// the output file writer can assume there is no linker script special rule + /// to handle. + bool hasLayoutCommands() const { return _layoutCommands.size() > 0; } + + /// Return true if this section has a mapping rule in the linker script + bool hasMapping(const SectionKey &key) const { + return getLayoutOrder(key, true) >= 0; + } + + /// Order function - used to sort input sections in the output file according + /// to linker script custom mappings. Return true if lhs should appear before + /// rhs. + bool less(const SectionKey &lhs, const SectionKey &rhs) const; + + /// Retrieve the name of the output section that this input section is mapped + /// to, according to custom linker script mappings. + StringRef getOutputSection(const SectionKey &key) const; + + /// Retrieve all the linker script expressions that need to be evaluated + /// before the given section is emitted. This is *not* const because the + /// first section to retrieve a given set of expression is the only one to + /// receive it. This set is marked as "delivered" and no other sections can + /// retrieve this set again. If we don't do this, multiple sections may map + /// to the same set of expressions because of wildcards rules. + std::vector<const SymbolAssignment *> getExprs(const SectionKey &key); + + /// Evaluate a single linker script expression according to our current + /// context (symbol table). This function is *not* constant because it can + /// update our symbol table with new symbols calculated in this expression. + std::error_code evalExpr(const SymbolAssignment *assgn, uint64_t &curPos); + + /// Retrieve the set of symbols defined in linker script expressions. + const llvm::StringSet<> &getScriptDefinedSymbols() const; + + /// Queries the linker script symbol table for the value of a given symbol. + /// This function must be called after linker script expressions evaluation + /// has been performed (by calling evalExpr() for all expressions). + uint64_t getLinkerScriptExprValue(StringRef name) const; + + void dump() const; + +private: + /// A custom hash operator to teach the STL how to handle our custom keys. + /// This will be used in our hash table mapping Sections to a Layout Order + /// number (caching results). + struct SectionKeyHash { + int64_t operator()(const SectionKey &k) const { + return llvm::hash_combine(k.archivePath, k.memberPath, k.sectionName); + } + }; + + /// Teach the STL when two section keys are the same. This will be used in + /// our hash table mapping Sections to a Layout Order number (caching results) + struct SectionKeyEq { + bool operator()(const SectionKey &lhs, const SectionKey &rhs) const { + return ((lhs.archivePath == rhs.archivePath) && + (lhs.memberPath == rhs.memberPath) && + (lhs.sectionName == rhs.sectionName)); + } + }; + + /// Given an order id, check if it matches the tuple + /// <archivePath, memberPath, sectionName> and returns the + /// internal id that matched, or -1 if no matches. + int matchSectionName(int id, const SectionKey &key) const; + + /// Returns a number that will determine the order of this input section + /// in the final layout. If coarse is true, we simply return the layour order + /// of the higher-level node InputSectionsCmd, used to order input sections. + /// If coarse is false, we return the layout index down to the internal + /// InputSectionsCmd arrangement, used to get the set of preceding linker + ///expressions. + int getLayoutOrder(const SectionKey &key, bool coarse) const; + + /// Compare two sections that have the same mapping rule (i.e., are matched + /// by the same InputSectionsCmd). + /// Determine if lhs < rhs by analyzing the InputSectionsCmd structure. + bool localCompare(int order, const SectionKey &lhs, + const SectionKey &rhs) const; + + + /// Our goal with all linearizeAST overloaded functions is to + /// traverse the linker script AST while putting nodes in a vector and + /// thus enforcing order among nodes (which comes first). + /// + /// The order among nodes is determined by their indexes in this vector + /// (_layoutCommands). This index allows us to solve the problem of + /// establishing the order among two different input sections: we match each + /// input sections with their respective layout command and use the indexes + /// of these commands to order these sections. + /// + /// Example: + /// + /// Given the linker script: + /// SECTIONS { + /// .text : { *(.text) } + /// .data : { *(.data) } + /// } + /// + /// The _layoutCommands vector should contain: + /// id 0 : <OutputSectionDescription> (_sectionName = ".text") + /// id 1 : <InputSectionsCmd> (_memberName = "*") + /// id 2 : <InputSectionName> (_name = ".text) + /// id 3 : <OutputSectionDescription> (_sectionName = ".data") + /// id 4 : <InputSectionsCmd> (_memberName = "*") + /// id 5 : <InputSectionName> (_name = ".data") + /// + /// If we need to sort the following input sections: + /// + /// input section A: .text from libc.a (member errno.o) + /// input section B: .data from libc.a (member write.o) + /// + /// Then we match input section A with the InputSectionsCmd of id 1, and + /// input section B with the InputSectionsCmd of id 4. Since 1 < 4, we + /// put A before B. + /// + /// The second problem handled by the linearization of the AST is the task + /// of finding all preceding expressions that need to be calculated before + /// emitting a given section. This task is easier to deal with when all nodes + /// are in a vector because otherwise we would need to traverse multiple + /// levels of the AST to find the set of expressions that preceed a layout + /// command. + /// + /// The linker script commands that are linearized ("layout commands") are: + /// + /// * OutputSectionDescription, containing an output section name + /// * InputSectionsCmd, containing an input file name + /// * InputSectionName, containing a single input section name + /// * InputSectionSortedName, a group of input section names + /// * SymbolAssignment, containing an expression that may + /// change the address where the linker is outputting data + /// + void linearizeAST(const Sections *sections); + void linearizeAST(const InputSectionsCmd *inputSections); + void linearizeAST(const InputSection *inputSection); + + void perform(const LinkerScript *ls); + + std::vector<std::unique_ptr<Parser>> _scripts; + std::vector<const Command *> _layoutCommands; + std::unordered_multimap<std::string, int> _memberToLayoutOrder; + std::vector<std::pair<StringRef, int>> _memberNameWildcards; + mutable std::unordered_map<SectionKey, int, SectionKeyHash, SectionKeyEq> + _cacheSectionOrder, _cacheExpressionOrder; + llvm::DenseSet<int> _deliveredExprs; + mutable llvm::StringSet<> _definedSymbols; + + Expression::SymbolTableTy _symbolTable; +}; + +llvm::BumpPtrAllocator &Command::getAllocator() const { + return _ctx.getAllocator(); +} +llvm::BumpPtrAllocator &Expression::getAllocator() const { + return _ctx.getAllocator(); +} +} // end namespace script +} // end namespace lld + +#endif diff --git a/include/lld/ReaderWriter/MachOLinkingContext.h b/include/lld/ReaderWriter/MachOLinkingContext.h new file mode 100644 index 0000000000000..8e253a1235f13 --- /dev/null +++ b/include/lld/ReaderWriter/MachOLinkingContext.h @@ -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 diff --git a/include/lld/ReaderWriter/PECOFFLinkingContext.h b/include/lld/ReaderWriter/PECOFFLinkingContext.h new file mode 100644 index 0000000000000..cccb8ac03b6e3 --- /dev/null +++ b/include/lld/ReaderWriter/PECOFFLinkingContext.h @@ -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 diff --git a/include/lld/ReaderWriter/RelocationHelperFunctions.h b/include/lld/ReaderWriter/RelocationHelperFunctions.h new file mode 100644 index 0000000000000..8738e91ebabc6 --- /dev/null +++ b/include/lld/ReaderWriter/RelocationHelperFunctions.h @@ -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 diff --git a/include/lld/ReaderWriter/YamlContext.h b/include/lld/ReaderWriter/YamlContext.h new file mode 100644 index 0000000000000..a15a398ec636a --- /dev/null +++ b/include/lld/ReaderWriter/YamlContext.h @@ -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 diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100644 index 0000000000000..699f5e93f8af6 --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(Config) +add_subdirectory(Core) +add_subdirectory(Driver) +add_subdirectory(ReaderWriter) diff --git a/lib/Config/CMakeLists.txt b/lib/Config/CMakeLists.txt new file mode 100644 index 0000000000000..f7ea0423b2c99 --- /dev/null +++ b/lib/Config/CMakeLists.txt @@ -0,0 +1,5 @@ +add_llvm_library(lldConfig + Version.cpp + LINK_LIBS + LLVMSupport + ) diff --git a/lib/Config/Makefile b/lib/Config/Makefile new file mode 100644 index 0000000000000..b3c57f81418ff --- /dev/null +++ b/lib/Config/Makefile @@ -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 diff --git a/lib/Config/Version.cpp b/lib/Config/Version.cpp new file mode 100644 index 0000000000000..b64ccef12c7b3 --- /dev/null +++ b/lib/Config/Version.cpp @@ -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 diff --git a/lib/Core/CMakeLists.txt b/lib/Core/CMakeLists.txt new file mode 100644 index 0000000000000..009b50a38335a --- /dev/null +++ b/lib/Core/CMakeLists.txt @@ -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 + ) diff --git a/lib/Core/DefinedAtom.cpp b/lib/Core/DefinedAtom.cpp new file mode 100644 index 0000000000000..b3f81ca65a914 --- /dev/null +++ b/lib/Core/DefinedAtom.cpp @@ -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 diff --git a/lib/Core/Error.cpp b/lib/Core/Error.cpp new file mode 100644 index 0000000000000..24809c3869e5c --- /dev/null +++ b/lib/Core/Error.cpp @@ -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); +} + +} diff --git a/lib/Core/File.cpp b/lib/Core/File.cpp new file mode 100644 index 0000000000000..dbac86b368aae --- /dev/null +++ b/lib/Core/File.cpp @@ -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 diff --git a/lib/Core/LinkingContext.cpp b/lib/Core/LinkingContext.cpp new file mode 100644 index 0000000000000..c6656b9359165 --- /dev/null +++ b/lib/Core/LinkingContext.cpp @@ -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 diff --git a/lib/Core/Makefile b/lib/Core/Makefile new file mode 100644 index 0000000000000..042d01a1e1b3b --- /dev/null +++ b/lib/Core/Makefile @@ -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 diff --git a/lib/Core/Reader.cpp b/lib/Core/Reader.cpp new file mode 100644 index 0000000000000..6f8b8cbd1bf8d --- /dev/null +++ b/lib/Core/Reader.cpp @@ -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 diff --git a/lib/Core/Resolver.cpp b/lib/Core/Resolver.cpp new file mode 100644 index 0000000000000..393a7ef2bfc86 --- /dev/null +++ b/lib/Core/Resolver.cpp @@ -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 diff --git a/lib/Core/SymbolTable.cpp b/lib/Core/SymbolTable.cpp new file mode 100644 index 0000000000000..f3f2da9262e08 --- /dev/null +++ b/lib/Core/SymbolTable.cpp @@ -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 diff --git a/lib/Core/TODO.txt b/lib/Core/TODO.txt new file mode 100644 index 0000000000000..196a3e02c2fcc --- /dev/null +++ b/lib/Core/TODO.txt @@ -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. + diff --git a/lib/Core/Writer.cpp b/lib/Core/Writer.cpp new file mode 100644 index 0000000000000..39bcc9e68523b --- /dev/null +++ b/lib/Core/Writer.cpp @@ -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 diff --git a/lib/Driver/CMakeLists.txt b/lib/Driver/CMakeLists.txt new file mode 100644 index 0000000000000..5a410e7eed7e9 --- /dev/null +++ b/lib/Driver/CMakeLists.txt @@ -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) + diff --git a/lib/Driver/CoreDriver.cpp b/lib/Driver/CoreDriver.cpp new file mode 100644 index 0000000000000..b8adee55746f4 --- /dev/null +++ b/lib/Driver/CoreDriver.cpp @@ -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 diff --git a/lib/Driver/CoreOptions.td b/lib/Driver/CoreOptions.td new file mode 100644 index 0000000000000..df7cb41737d23 --- /dev/null +++ b/lib/Driver/CoreOptions.td @@ -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">; + diff --git a/lib/Driver/DarwinLdDriver.cpp b/lib/Driver/DarwinLdDriver.cpp new file mode 100644 index 0000000000000..2c64aeee38a58 --- /dev/null +++ b/lib/Driver/DarwinLdDriver.cpp @@ -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 diff --git a/lib/Driver/DarwinLdOptions.td b/lib/Driver/DarwinLdOptions.td new file mode 100644 index 0000000000000..81dcc0a1d9251 --- /dev/null +++ b/lib/Driver/DarwinLdOptions.td @@ -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>; diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp new file mode 100644 index 0000000000000..d32bfa6e47beb --- /dev/null +++ b/lib/Driver/Driver.cpp @@ -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 diff --git a/lib/Driver/GnuLdDriver.cpp b/lib/Driver/GnuLdDriver.cpp new file mode 100644 index 0000000000000..b9af04d4b6152 --- /dev/null +++ b/lib/Driver/GnuLdDriver.cpp @@ -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()); +} diff --git a/lib/Driver/GnuLdOptions.td b/lib/Driver/GnuLdOptions.td new file mode 100644 index 0000000000000..9d06f29354395 --- /dev/null +++ b/lib/Driver/GnuLdOptions.td @@ -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">; diff --git a/lib/Driver/Makefile b/lib/Driver/Makefile new file mode 100644 index 0000000000000..19024cfab0f16 --- /dev/null +++ b/lib/Driver/Makefile @@ -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, $@) $< diff --git a/lib/Driver/TODO.rst b/lib/Driver/TODO.rst new file mode 100644 index 0000000000000..e03d829c232dd --- /dev/null +++ b/lib/Driver/TODO.rst @@ -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 diff --git a/lib/Driver/UniversalDriver.cpp b/lib/Driver/UniversalDriver.cpp new file mode 100644 index 0000000000000..7d42ad7b4bfc0 --- /dev/null +++ b/lib/Driver/UniversalDriver.cpp @@ -0,0 +1,218 @@ +//===- lib/Driver/UniversalDriver.cpp -------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// Driver for "universal" lld tool which can mimic any linker command line +/// parsing once it figures out which command line flavor to use. +/// +//===----------------------------------------------------------------------===// + +#include "lld/Driver/Driver.h" +#include "lld/Config/Version.h" +#include "lld/Core/LLVM.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace lld; + +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 "UniversalDriverOptions.inc" +#undef OPTION +}; + +// Create prefix string literals used in GnuLdOptions.td +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "UniversalDriverOptions.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 "UniversalDriverOptions.inc" +#undef OPTION +}; + +// Create OptTable class for parsing actual command line arguments +class UniversalDriverOptTable : public llvm::opt::OptTable { +public: + UniversalDriverOptTable() + : OptTable(infoTable, llvm::array_lengthof(infoTable)) {} +}; + +enum class Flavor { + invalid, + gnu_ld, // -flavor gnu + win_link, // -flavor link + darwin_ld, // -flavor darwin + core // -flavor core OR -core +}; + +struct ProgramNameParts { + StringRef _target; + StringRef _flavor; +}; + +} // anonymous namespace + +static Flavor strToFlavor(StringRef str) { + return llvm::StringSwitch<Flavor>(str) + .Case("gnu", Flavor::gnu_ld) + .Case("link", Flavor::win_link) + .Case("lld-link", Flavor::win_link) + .Case("darwin", Flavor::darwin_ld) + .Case("core", Flavor::core) + .Case("ld", Flavor::gnu_ld) + .Default(Flavor::invalid); +} + +static ProgramNameParts parseProgramName(StringRef programName) { + SmallVector<StringRef, 3> components; + llvm::SplitString(programName, components, "-"); + ProgramNameParts ret; + + using std::begin; + using std::end; + + // Erase any lld components. + components.erase(std::remove(components.begin(), components.end(), "lld"), + components.end()); + + // Find the flavor component. + auto flIter = std::find_if(components.begin(), components.end(), + [](StringRef str) -> bool { + return strToFlavor(str) != Flavor::invalid; + }); + + if (flIter != components.end()) { + ret._flavor = *flIter; + components.erase(flIter); + } + + // Any remaining component must be the target. + if (components.size() == 1) + ret._target = components[0]; + + return ret; +} + +// Removes the argument from argv along with its value, if exists, and updates +// argc. +static void removeArg(llvm::opt::Arg *arg, int &argc, const char **&argv) { + unsigned int numToRemove = arg->getNumValues() + 1; + unsigned int argIndex = arg->getIndex() + 1; + + std::rotate(&argv[argIndex], &argv[argIndex + numToRemove], argv + argc); + argc -= numToRemove; +} + +static Flavor getFlavor(int &argc, const char **&argv, + std::unique_ptr<llvm::opt::InputArgList> &parsedArgs) { + if (llvm::opt::Arg *argCore = parsedArgs->getLastArg(OPT_core)) { + removeArg(argCore, argc, argv); + return Flavor::core; + } + if (llvm::opt::Arg *argFlavor = parsedArgs->getLastArg(OPT_flavor)) { + removeArg(argFlavor, argc, argv); + return strToFlavor(argFlavor->getValue()); + } + +#if LLVM_ON_UNIX + if (llvm::sys::path::filename(argv[0]).equals("ld")) { +#if __APPLE__ + // On a Darwin systems, if linker binary is named "ld", use Darwin driver. + return Flavor::darwin_ld; +#endif + // On a ELF based systems, if linker binary is named "ld", use gnu driver. + return Flavor::gnu_ld; + } +#endif + + StringRef name = llvm::sys::path::stem(argv[0]); + return strToFlavor(parseProgramName(name)._flavor); +} + +namespace lld { + +bool UniversalDriver::link(int argc, const char *argv[], + raw_ostream &diagnostics) { + // Parse command line options using GnuLdOptions.td + std::unique_ptr<llvm::opt::InputArgList> parsedArgs; + UniversalDriverOptTable table; + unsigned missingIndex; + unsigned missingCount; + + // Program name + StringRef programName = llvm::sys::path::stem(argv[0]); + + 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; + } + + // Handle -help + if (parsedArgs->getLastArg(OPT_help)) { + table.PrintHelp(llvm::outs(), programName.data(), "LLVM Linker", false); + return true; + } + + // Handle -version + if (parsedArgs->getLastArg(OPT_version)) { + diagnostics << "LLVM Linker Version: " << getLLDVersion() + << getLLDRepositoryVersion() << "\n"; + return true; + } + + Flavor flavor = getFlavor(argc, argv, parsedArgs); + std::vector<const char *> args(argv, argv + argc); + + // Switch to appropriate driver. + switch (flavor) { + case Flavor::gnu_ld: + return GnuLdDriver::linkELF(args.size(), args.data(), diagnostics); + case Flavor::darwin_ld: + return DarwinLdDriver::linkMachO(args.size(), args.data(), diagnostics); + case Flavor::win_link: + return WinLinkDriver::linkPECOFF(args.size(), args.data(), diagnostics); + case Flavor::core: + return CoreDriver::link(args.size(), args.data(), diagnostics); + case Flavor::invalid: + diagnostics << "Select the appropriate flavor\n"; + table.PrintHelp(llvm::outs(), programName.data(), "LLVM Linker", false); + return false; + } + llvm_unreachable("Unrecognised flavor"); +} + +} // end namespace lld diff --git a/lib/Driver/UniversalDriverOptions.td b/lib/Driver/UniversalDriverOptions.td new file mode 100644 index 0000000000000..14abc9ce9911d --- /dev/null +++ b/lib/Driver/UniversalDriverOptions.td @@ -0,0 +1,19 @@ +include "llvm/Option/OptParser.td" + +// Select an optional flavor +def flavor: Separate<["-"], "flavor">, + HelpText<"Flavor for linking, options are gnu/darwin/link">; + +// Select the core flavor +def core : Flag<["-"], "core">, + HelpText<"CORE linking">; + +def target: Separate<["-"], "target">, + HelpText<"Select the target">; + +def version: Flag<["-"], "version">, + HelpText<"Display the version">; + +// Help message +def help : Flag<["-"], "help">, + HelpText<"Display this help message">; diff --git a/lib/Driver/WinLinkDriver.cpp b/lib/Driver/WinLinkDriver.cpp new file mode 100644 index 0000000000000..6ee7a5a004b5a --- /dev/null +++ b/lib/Driver/WinLinkDriver.cpp @@ -0,0 +1,1371 @@ +//===- lib/Driver/WinLinkDriver.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 Windows link.exe. +/// +//===----------------------------------------------------------------------===// + +#include "lld/Driver/Driver.h" +#include "lld/Driver/WinLinkModuleDef.h" +#include "lld/ReaderWriter/PECOFFLinkingContext.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Object/COFF.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cctype> +#include <map> +#include <memory> +#include <sstream> +#include <tuple> + +namespace lld { + +// +// Option definitions +// + +// Create enum with OPT_xxx values for each option in WinLinkOptions.td +enum { + OPT_INVALID = 0, +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELP, META) \ + OPT_##ID, +#include "WinLinkOptions.inc" +#undef OPTION +}; + +// Create prefix string literals used in WinLinkOptions.td +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "WinLinkOptions.inc" +#undef PREFIX + +// Create table mapping all options defined in WinLinkOptions.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 "WinLinkOptions.inc" +#undef OPTION +}; + +namespace { + +// Create OptTable class for parsing actual command line arguments +class WinLinkOptTable : public llvm::opt::OptTable { +public: + // link.exe's command line options are case insensitive, unlike + // other driver's options for Unix. + WinLinkOptTable() + : OptTable(infoTable, llvm::array_lengthof(infoTable), + /* ignoreCase */ true) {} +}; + +} // anonymous namespace + +// +// Functions to parse each command line option +// + +// Split the given string with spaces. +static std::vector<std::string> splitArgList(const std::string &str) { + std::stringstream stream(str); + std::istream_iterator<std::string> begin(stream); + std::istream_iterator<std::string> end; + return std::vector<std::string>(begin, end); +} + +// Split the given string with the path separator. +static std::vector<StringRef> splitPathList(StringRef str) { + std::vector<StringRef> ret; + while (!str.empty()) { + StringRef path; + std::tie(path, str) = str.split(';'); + ret.push_back(path); + } + return ret; +} + +// Parse an argument for /alternatename. The expected string is +// "<string>=<string>". +static bool parseAlternateName(StringRef arg, StringRef &weak, StringRef &def, + raw_ostream &diag) { + std::tie(weak, def) = arg.split('='); + if (weak.empty() || def.empty()) { + diag << "Error: malformed /alternatename option: " << arg << "\n"; + return false; + } + return true; +} + +// Parse an argument for /base, /stack or /heap. The expected string +// is "<integer>[,<integer>]". +static bool parseMemoryOption(StringRef arg, uint64_t &reserve, + uint64_t &commit) { + StringRef reserveStr, commitStr; + std::tie(reserveStr, commitStr) = arg.split(','); + if (reserveStr.getAsInteger(0, reserve)) + return false; + if (!commitStr.empty() && commitStr.getAsInteger(0, commit)) + return false; + return true; +} + +// Parse an argument for /version or /subsystem. The expected string is +// "<integer>[.<integer>]". +static bool parseVersion(StringRef arg, uint32_t &major, uint32_t &minor) { + StringRef majorVersion, minorVersion; + std::tie(majorVersion, minorVersion) = arg.split('.'); + if (minorVersion.empty()) + minorVersion = "0"; + if (majorVersion.getAsInteger(0, major)) + return false; + if (minorVersion.getAsInteger(0, minor)) + return false; + return true; +} + +// Returns subsystem type for the given string. +static llvm::COFF::WindowsSubsystem stringToWinSubsystem(StringRef str) { + return llvm::StringSwitch<llvm::COFF::WindowsSubsystem>(str.lower()) + .Case("windows", llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_GUI) + .Case("console", llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI) + .Case("boot_application", + llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION) + .Case("efi_application", llvm::COFF::IMAGE_SUBSYSTEM_EFI_APPLICATION) + .Case("efi_boot_service_driver", + llvm::COFF::IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) + .Case("efi_rom", llvm::COFF::IMAGE_SUBSYSTEM_EFI_ROM) + .Case("efi_runtime_driver", + llvm::COFF::IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) + .Case("native", llvm::COFF::IMAGE_SUBSYSTEM_NATIVE) + .Case("posix", llvm::COFF::IMAGE_SUBSYSTEM_POSIX_CUI) + .Default(llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN); +} + +// Parse /subsystem command line option. The form of /subsystem is +// "subsystem_name[,majorOSVersion[.minorOSVersion]]". +static bool parseSubsystem(StringRef arg, + llvm::COFF::WindowsSubsystem &subsystem, + llvm::Optional<uint32_t> &major, + llvm::Optional<uint32_t> &minor, raw_ostream &diag) { + StringRef subsystemStr, osVersion; + std::tie(subsystemStr, osVersion) = arg.split(','); + if (!osVersion.empty()) { + uint32_t v1, v2; + if (!parseVersion(osVersion, v1, v2)) + return false; + major = v1; + minor = v2; + } + subsystem = stringToWinSubsystem(subsystemStr); + if (subsystem == llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN) { + diag << "error: unknown subsystem name: " << subsystemStr << "\n"; + return false; + } + return true; +} + +static llvm::COFF::MachineTypes stringToMachineType(StringRef str) { + // FIXME: we have no way to differentiate between ARM and ARMNT currently. + // However, given that LLVM only supports ARM NT, default to that for now. + return llvm::StringSwitch<llvm::COFF::MachineTypes>(str.lower()) + .Case("arm", llvm::COFF::IMAGE_FILE_MACHINE_ARMNT) + .Case("x64", llvm::COFF::IMAGE_FILE_MACHINE_AMD64) + .Case("x86", llvm::COFF::IMAGE_FILE_MACHINE_I386) + .Default(llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN); +} + +// Parse /section:name,[[!]{DEKPRSW}] +// +// /section option is to set non-default bits in the Characteristics fields of +// the section header. D, E, K, P, R, S, and W represent discardable, +// execute, not_cachable, not_pageable, read, shared, and write bits, +// respectively. You can specify multiple flags in one /section option. +// +// If the flag starts with "!", the flags represent a mask that should be turned +// off regardless of the default value. You can even create a section which is +// not readable, writable nor executable with this -- although it's probably +// useless. +static bool parseSection(StringRef option, std::string §ion, + llvm::Optional<uint32_t> &flags, + llvm::Optional<uint32_t> &mask) { + StringRef flagString; + std::tie(section, flagString) = option.split(","); + + bool negative = false; + if (flagString.startswith("!")) { + negative = true; + flagString = flagString.substr(1); + } + if (flagString.empty()) + return false; + + uint32_t attribs = 0; + for (size_t i = 0, e = flagString.size(); i < e; ++i) { + switch (tolower(flagString[i])) { +#define CASE(c, flag) \ + case c: \ + attribs |= flag; \ + break + CASE('d', llvm::COFF::IMAGE_SCN_MEM_DISCARDABLE); + CASE('e', llvm::COFF::IMAGE_SCN_MEM_EXECUTE); + CASE('k', llvm::COFF::IMAGE_SCN_MEM_NOT_CACHED); + CASE('p', llvm::COFF::IMAGE_SCN_MEM_NOT_PAGED); + CASE('r', llvm::COFF::IMAGE_SCN_MEM_READ); + CASE('s', llvm::COFF::IMAGE_SCN_MEM_SHARED); + CASE('w', llvm::COFF::IMAGE_SCN_MEM_WRITE); +#undef CASE + default: + return false; + } + } + + if (negative) { + mask = attribs; + } else { + flags = attribs; + } + return true; +} + +static bool readFile(PECOFFLinkingContext &ctx, StringRef path, + ArrayRef<uint8_t> &result) { + ErrorOr<std::unique_ptr<MemoryBuffer>> buf = MemoryBuffer::getFile(path); + if (!buf) + return false; + StringRef Data = buf.get()->getBuffer(); + result = ctx.allocate(ArrayRef<uint8_t>( + reinterpret_cast<const uint8_t *>(Data.begin()), Data.size())); + return true; +} + +// Parse /manifest:EMBED[,ID=#]|NO. +static bool parseManifest(StringRef option, bool &enable, bool &embed, + int &id) { + if (option.equals_lower("no")) { + enable = false; + return true; + } + if (!option.startswith_lower("embed")) + return false; + + embed = true; + option = option.substr(strlen("embed")); + if (option.empty()) + return true; + if (!option.startswith_lower(",id=")) + return false; + option = option.substr(strlen(",id=")); + if (option.getAsInteger(0, id)) + return false; + return true; +} + +static bool isLibraryFile(StringRef path) { + return path.endswith_lower(".lib") || path.endswith_lower(".imp"); +} + +static StringRef getObjectPath(PECOFFLinkingContext &ctx, StringRef path) { + std::string result; + if (isLibraryFile(path)) { + result = ctx.searchLibraryFile(path); + } else if (llvm::sys::path::extension(path).empty()) { + result = path.str() + ".obj"; + } else { + result = path; + } + return ctx.allocate(result); +} + +static StringRef getLibraryPath(PECOFFLinkingContext &ctx, StringRef path) { + std::string result = isLibraryFile(path) + ? ctx.searchLibraryFile(path) + : ctx.searchLibraryFile(path.str() + ".lib"); + return ctx.allocate(result); +} + +// Returns true if the given file is a Windows resource file. +static bool isResoruceFile(StringRef path) { + llvm::sys::fs::file_magic fileType; + if (llvm::sys::fs::identify_magic(path, fileType)) { + // If we cannot read the file, assume it's not a resource file. + // The further stage will raise an error on this unreadable file. + return false; + } + return fileType == llvm::sys::fs::file_magic::windows_resource; +} + +// Merge Windows resource files and convert them to a single COFF file. +// The temporary file path is set to result. +static bool convertResourceFiles(PECOFFLinkingContext &ctx, + std::vector<std::string> inFiles, + std::string &result) { + // Create an output file path. + SmallString<128> outFile; + if (llvm::sys::fs::createTemporaryFile("resource", "obj", outFile)) + return false; + std::string outFileArg = ("/out:" + outFile).str(); + + // Construct CVTRES.EXE command line and execute it. + std::string program = "cvtres.exe"; + ErrorOr<std::string> programPathOrErr = llvm::sys::findProgramByName(program); + if (!programPathOrErr) { + llvm::errs() << "Unable to find " << program << " in PATH\n"; + return false; + } + const std::string &programPath = *programPathOrErr; + + std::vector<const char *> args; + args.push_back(programPath.c_str()); + args.push_back(ctx.is64Bit() ? "/machine:x64" : "/machine:x86"); + args.push_back("/readonly"); + args.push_back("/nologo"); + args.push_back(outFileArg.c_str()); + for (const std::string &path : inFiles) + args.push_back(path.c_str()); + args.push_back(nullptr); + + if (llvm::sys::ExecuteAndWait(programPath.c_str(), &args[0]) != 0) { + llvm::errs() << program << " failed\n"; + return false; + } + result = outFile.str(); + return true; +} + +// Parse /manifestuac:(level=<string>|uiAccess=<string>). +// +// The arguments will be embedded to the manifest XML file with no error check, +// so the values given via the command line must be valid as XML attributes. +// This may sound a bit odd, but that's how link.exe works, so we will follow. +static bool parseManifestUAC(StringRef option, + llvm::Optional<std::string> &level, + llvm::Optional<std::string> &uiAccess) { + for (;;) { + option = option.ltrim(); + if (option.empty()) + return true; + if (option.startswith_lower("level=")) { + option = option.substr(strlen("level=")); + StringRef value; + std::tie(value, option) = option.split(" "); + level = value.str(); + continue; + } + if (option.startswith_lower("uiaccess=")) { + option = option.substr(strlen("uiaccess=")); + StringRef value; + std::tie(value, option) = option.split(" "); + uiAccess = value.str(); + continue; + } + return false; + } +} + +// Returns the machine type (e.g. x86) of the given input file. +// If the file is not COFF, returns false. +static bool getMachineType(StringRef path, llvm::COFF::MachineTypes &result) { + llvm::sys::fs::file_magic fileType; + if (llvm::sys::fs::identify_magic(path, fileType)) + return false; + if (fileType != llvm::sys::fs::file_magic::coff_object) + return false; + ErrorOr<std::unique_ptr<MemoryBuffer>> buf = MemoryBuffer::getFile(path); + if (!buf) + return false; + std::error_code ec; + llvm::object::COFFObjectFile obj(buf.get()->getMemBufferRef(), ec); + if (ec) + return false; + result = static_cast<llvm::COFF::MachineTypes>(obj.getMachine()); + return true; +} + +// Parse /export:entryname[=internalname][,@ordinal[,NONAME]][,DATA][,PRIVATE]. +// +// MSDN doesn't say anything about /export:foo=bar style option or PRIVATE +// attribtute, but link.exe actually accepts them. +static bool parseExport(StringRef option, + PECOFFLinkingContext::ExportDesc &ret) { + StringRef name; + StringRef rest; + std::tie(name, rest) = option.split(","); + if (name.empty()) + return false; + if (name.find('=') == StringRef::npos) { + ret.name = name; + } else { + std::tie(ret.externalName, ret.name) = name.split("="); + if (ret.name.empty()) + return false; + } + + for (;;) { + if (rest.empty()) + return true; + StringRef arg; + std::tie(arg, rest) = rest.split(","); + if (arg.equals_lower("noname")) { + if (ret.ordinal < 0) + return false; + ret.noname = true; + continue; + } + if (arg.equals_lower("data")) { + ret.isData = true; + continue; + } + if (arg.equals_lower("private")) { + ret.isPrivate = true; + continue; + } + if (arg.startswith("@")) { + int ordinal; + if (arg.substr(1).getAsInteger(0, ordinal)) + return false; + if (ordinal <= 0 || 65535 < ordinal) + return false; + ret.ordinal = ordinal; + continue; + } + return false; + } +} + +// Read module-definition file. +static bool parseDef(StringRef option, llvm::BumpPtrAllocator &alloc, + std::vector<moduledef::Directive *> &result) { + ErrorOr<std::unique_ptr<MemoryBuffer>> buf = MemoryBuffer::getFile(option); + if (!buf) + return false; + moduledef::Lexer lexer(std::move(buf.get())); + moduledef::Parser parser(lexer, alloc); + return parser.parse(result); +} + +static StringRef replaceExtension(PECOFFLinkingContext &ctx, StringRef path, + StringRef extension) { + SmallString<128> val = path; + llvm::sys::path::replace_extension(val, extension); + return ctx.allocate(val.str()); +} + +// Create a manifest file contents. +static std::string createManifestXml(PECOFFLinkingContext &ctx) { + std::string ret; + llvm::raw_string_ostream out(ret); + // Emit the XML. Note that we do *not* verify that the XML attributes are + // syntactically correct. This is intentional for link.exe compatibility. + out << "<?xml version=\"1.0\" standalone=\"yes\"?>\n" + "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\"\n" + " manifestVersion=\"1.0\">\n"; + if (ctx.getManifestUAC()) { + out << " <trustInfo>\n" + " <security>\n" + " <requestedPrivileges>\n" + " <requestedExecutionLevel level=" << ctx.getManifestLevel() + << " uiAccess=" << ctx.getManifestUiAccess() + << "/>\n" + " </requestedPrivileges>\n" + " </security>\n" + " </trustInfo>\n"; + const std::string &dependency = ctx.getManifestDependency(); + if (!dependency.empty()) { + out << " <dependency>\n" + " <dependentAssembly>\n" + " <assemblyIdentity " << dependency + << " />\n" + " </dependentAssembly>\n" + " </dependency>\n"; + } + } + out << "</assembly>\n"; + out.flush(); + return ret; +} + +// Convert one doublequote to two doublequotes, so that we can embed the string +// into a resource script file. +static void quoteAndPrintXml(raw_ostream &out, StringRef str) { + for (;;) { + if (str.empty()) + return; + StringRef line; + std::tie(line, str) = str.split("\n"); + if (line.empty()) + continue; + out << '\"'; + const char *p = line.data(); + for (int i = 0, size = line.size(); i < size; ++i) { + switch (p[i]) { + case '\"': + out << '\"'; + // fallthrough + default: + out << p[i]; + } + } + out << "\"\n"; + } +} + +// Create a resource file (.res file) containing the manifest XML. This is done +// in two steps: +// +// 1. Create a resource script file containing the XML as a literal string. +// 2. Run RC.EXE command to compile the script file to a resource file. +// +// The temporary file created in step 1 will be deleted on exit from this +// function. The file created in step 2 will have the same lifetime as the +// PECOFFLinkingContext. +static bool createManifestResourceFile(PECOFFLinkingContext &ctx, + raw_ostream &diag, + std::string &resFile) { + // Create a temporary file for the resource script file. + SmallString<128> rcFileSmallString; + if (llvm::sys::fs::createTemporaryFile("tmp", "rc", rcFileSmallString)) { + diag << "Cannot create a temporary file\n"; + return false; + } + StringRef rcFile(rcFileSmallString.str()); + llvm::FileRemover rcFileRemover((Twine(rcFile))); + + // Open the temporary file for writing. + std::error_code ec; + llvm::raw_fd_ostream out(rcFileSmallString, ec, llvm::sys::fs::F_Text); + if (ec) { + diag << "Failed to open " << ctx.getManifestOutputPath() << ": " + << ec.message() << "\n"; + return false; + } + + // Write resource script to the RC file. + out << "#define LANG_ENGLISH 9\n" + << "#define SUBLANG_DEFAULT 1\n" + << "#define APP_MANIFEST " << ctx.getManifestId() << "\n" + << "#define RT_MANIFEST 24\n" + << "LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT\n" + << "APP_MANIFEST RT_MANIFEST {\n"; + quoteAndPrintXml(out, createManifestXml(ctx)); + out << "}\n"; + out.close(); + + // Create output resource file. + SmallString<128> resFileSmallString; + if (llvm::sys::fs::createTemporaryFile("tmp", "res", resFileSmallString)) { + diag << "Cannot create a temporary file"; + return false; + } + resFile = resFileSmallString.str(); + + // Register the resource file path so that the file will be deleted when the + // context's destructor is called. + ctx.registerTemporaryFile(resFile); + + // Run RC.EXE /fo tmp.res tmp.rc + std::string program = "rc.exe"; + ErrorOr<std::string> programPathOrErr = llvm::sys::findProgramByName(program); + if (!programPathOrErr) { + diag << "Unable to find " << program << " in PATH\n"; + return false; + } + const std::string &programPath = *programPathOrErr; + std::vector<const char *> args; + args.push_back(programPath.c_str()); + args.push_back("/fo"); + args.push_back(resFile.c_str()); + args.push_back("/nologo"); + args.push_back(rcFileSmallString.c_str()); + args.push_back(nullptr); + + if (llvm::sys::ExecuteAndWait(programPath.c_str(), &args[0]) != 0) { + diag << program << " failed\n"; + return false; + } + return true; +} + + +// Create the a side-by-side manifest file. +// +// The manifest file will convey some information to the linker, such as whether +// the binary needs to run as Administrator or not. Instead of being placed in +// the PE/COFF header, it's in XML format for some reason -- I guess it's +// probably because it's invented in the early dot-com era. +// +// The side-by-side manifest file is a separate XML file having ".manifest" +// extension. It will be created in the same directory as the resulting +// executable. +static bool createSideBySideManifestFile(PECOFFLinkingContext &ctx, + raw_ostream &diag) { + std::string path = ctx.getManifestOutputPath(); + if (path.empty()) { + // Default name of the manifest file is "foo.exe.manifest" where "foo.exe" is + // the output path. + path = ctx.outputPath(); + path.append(".manifest"); + } + + std::error_code ec; + llvm::raw_fd_ostream out(path, ec, llvm::sys::fs::F_Text); + if (ec) { + diag << ec.message() << "\n"; + return false; + } + out << createManifestXml(ctx); + return true; +} + +// Handle /failifmismatch option. +static bool +handleFailIfMismatchOption(StringRef option, + std::map<StringRef, StringRef> &mustMatch, + raw_ostream &diag) { + StringRef key, value; + std::tie(key, value) = option.split('='); + if (key.empty() || value.empty()) { + diag << "error: malformed /failifmismatch option: " << option << "\n"; + return true; + } + auto it = mustMatch.find(key); + if (it != mustMatch.end() && it->second != value) { + diag << "error: mismatch detected: '" << it->second << "' and '" << value + << "' for key '" << key << "'\n"; + return true; + } + mustMatch[key] = value; + return false; +} + +// +// Environment variable +// + +// Process "LINK" environment variable. If defined, the value of the variable +// should be processed as command line arguments. +static std::vector<const char *> processLinkEnv(PECOFFLinkingContext &ctx, + int argc, const char **argv) { + std::vector<const char *> ret; + // The first argument is the name of the command. This should stay at the head + // of the argument list. + assert(argc > 0); + ret.push_back(argv[0]); + + // Add arguments specified by the LINK environment variable. + llvm::Optional<std::string> env = llvm::sys::Process::GetEnv("LINK"); + if (env.hasValue()) + for (std::string &arg : splitArgList(*env)) + ret.push_back(ctx.allocate(arg).data()); + + // Add the rest of arguments passed via the command line. + for (int i = 1; i < argc; ++i) + ret.push_back(argv[i]); + ret.push_back(nullptr); + return ret; +} + +// Process "LIB" environment variable. The variable contains a list of search +// paths separated by semicolons. +static void processLibEnv(PECOFFLinkingContext &ctx) { + llvm::Optional<std::string> env = llvm::sys::Process::GetEnv("LIB"); + if (env.hasValue()) + for (StringRef path : splitPathList(*env)) + ctx.appendInputSearchPath(ctx.allocate(path)); +} + +namespace { +class DriverStringSaver : public llvm::cl::StringSaver { +public: + DriverStringSaver(PECOFFLinkingContext &ctx) : _ctx(ctx) {} + + const char *SaveString(const char *s) override { + return _ctx.allocate(StringRef(s)).data(); + } + +private: + PECOFFLinkingContext &_ctx; +}; +} + +// Tokenize command line options in a given file and add them to result. +static bool readResponseFile(StringRef path, PECOFFLinkingContext &ctx, + std::vector<const char *> &result) { + ArrayRef<uint8_t> contents; + if (!readFile(ctx, path, contents)) + return false; + StringRef contentsStr(reinterpret_cast<const char *>(contents.data()), + contents.size()); + DriverStringSaver saver(ctx); + SmallVector<const char *, 0> args; + llvm::cl::TokenizeWindowsCommandLine(contentsStr, saver, args); + for (const char *s : args) + result.push_back(s); + return true; +} + +// Expand arguments starting with "@". It's an error if a specified file does +// not exist. Returns true on success. +static bool expandResponseFiles(int &argc, const char **&argv, + PECOFFLinkingContext &ctx, raw_ostream &diag, + bool &expanded) { + std::vector<const char *> newArgv; + for (int i = 0; i < argc; ++i) { + if (argv[i][0] != '@') { + newArgv.push_back(argv[i]); + continue; + } + StringRef filename = StringRef(argv[i] + 1); + if (!readResponseFile(filename, ctx, newArgv)) { + diag << "error: cannot read response file: " << filename << "\n"; + return false; + } + expanded = true; + } + if (!expanded) + return true; + argc = newArgv.size(); + newArgv.push_back(nullptr); + argv = &ctx.allocateCopy(newArgv)[0]; + return true; +} + +// Parses the given command line options and returns the result. Returns NULL if +// there's an error in the options. +static std::unique_ptr<llvm::opt::InputArgList> +parseArgs(int argc, const char **argv, PECOFFLinkingContext &ctx, + raw_ostream &diag, bool isReadingDirectiveSection) { + // Expand arguments starting with "@". + bool expanded = false; + if (!expandResponseFiles(argc, argv, ctx, diag, expanded)) + return nullptr; + + // Parse command line options using WinLinkOptions.td + std::unique_ptr<llvm::opt::InputArgList> parsedArgs; + WinLinkOptTable 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 nullptr; + } + + // Show warning for unknown arguments. In .drectve section, unknown options + // starting with "-?" are silently ignored. This is a COFF's feature to embed a + // new linker option to an object file while keeping backward compatibility. + for (auto unknownArg : parsedArgs->filtered(OPT_UNKNOWN)) { + StringRef arg = unknownArg->getSpelling(); + if (isReadingDirectiveSection && arg.startswith("-?")) + continue; + diag << "warning: ignoring unknown argument: " << arg << "\n"; + } + + // Copy mllvm + for (auto arg : parsedArgs->filtered(OPT_mllvm)) + ctx.appendLLVMOption(arg->getValue()); + + // If we have expaneded response files and /verbose is given, print out the + // final command line. + if (!isReadingDirectiveSection && expanded && + parsedArgs->getLastArg(OPT_verbose)) { + diag << "Command line:"; + for (int i = 0; i < argc; ++i) + diag << " " << argv[i]; + diag << "\n\n"; + } + + return parsedArgs; +} + +// Returns true if the given file node has already been added to the input +// graph. +static bool hasLibrary(PECOFFLinkingContext &ctx, File *file) { + StringRef path = file->path(); + for (std::unique_ptr<Node> &p : ctx.getNodes()) + if (auto *f = dyn_cast<FileNode>(p.get())) + if (f->getFile()->path() == path) + return true; + return false; +} + +// If the first command line argument is "/lib", link.exe acts as if it's +// "lib.exe" command. This is for backward compatibility. +// http://msdn.microsoft.com/en-us/library/h34w59b3.aspx +static bool maybeRunLibCommand(int argc, const char **argv, raw_ostream &diag) { + if (argc <= 1) + return false; + if (!StringRef(argv[1]).equals_lower("/lib")) + return false; + ErrorOr<std::string> pathOrErr = llvm::sys::findProgramByName("lib.exe"); + if (!pathOrErr) { + diag << "Unable to find lib.exe in PATH\n"; + return true; + } + const std::string &path = *pathOrErr; + + // Run lib.exe + std::vector<const char *> vec; + vec.push_back(path.c_str()); + for (int i = 2; i < argc; ++i) + vec.push_back(argv[i]); + vec.push_back(nullptr); + + if (llvm::sys::ExecuteAndWait(path.c_str(), &vec[0]) != 0) + diag << "lib.exe failed\n"; + return true; +} + +/// \brief Parse the input file to lld::File. +void addFiles(PECOFFLinkingContext &ctx, StringRef path, raw_ostream &diag, + std::vector<std::unique_ptr<File>> &files) { + for (std::unique_ptr<File> &file : loadFile(ctx, path, false)) { + if (ctx.logInputFiles()) + diag << file->path() << "\n"; + files.push_back(std::move(file)); + } +} + +// +// Main driver +// + +bool WinLinkDriver::linkPECOFF(int argc, const char **argv, raw_ostream &diag) { + if (maybeRunLibCommand(argc, argv, diag)) + return true; + + PECOFFLinkingContext ctx; + ctx.setParseDirectives(parseDirectives); + ctx.registry().addSupportCOFFObjects(ctx); + ctx.registry().addSupportCOFFImportLibraries(ctx); + ctx.registry().addSupportArchives(ctx.logInputFiles()); + ctx.registry().addSupportNativeObjects(); + ctx.registry().addSupportYamlFiles(); + + std::vector<const char *> newargv = processLinkEnv(ctx, argc, argv); + processLibEnv(ctx); + if (!parse(newargv.size() - 1, &newargv[0], ctx, diag)) + return false; + + // Create the file if needed. + if (ctx.getCreateManifest() && !ctx.getEmbedManifest()) + if (!createSideBySideManifestFile(ctx, diag)) + return false; + + return link(ctx, diag); +} + +bool WinLinkDriver::parse(int argc, const char *argv[], + PECOFFLinkingContext &ctx, raw_ostream &diag, + bool isReadingDirectiveSection) { + // Parse may be called from multiple threads simultaneously to parse .drectve + // sections. This function is not thread-safe because it mutates the context + // object. So acquire the lock. + std::lock_guard<std::recursive_mutex> lock(ctx.getMutex()); + + std::map<StringRef, StringRef> failIfMismatchMap; + // Parse the options. + std::unique_ptr<llvm::opt::InputArgList> parsedArgs = + parseArgs(argc, argv, ctx, diag, isReadingDirectiveSection); + if (!parsedArgs) + return false; + + // The list of input files. + std::vector<std::unique_ptr<File>> files; + std::vector<std::unique_ptr<File>> libraries; + + // Handle /help + if (parsedArgs->hasArg(OPT_help)) { + WinLinkOptTable table; + table.PrintHelp(llvm::outs(), argv[0], "LLVM Linker", false); + return false; + } + + // Handle /machine before parsing all the other options, as the target machine + // type affects how to handle other options. For example, x86 needs the + // leading underscore to mangle symbols, while x64 doesn't need it. + if (llvm::opt::Arg *inputArg = parsedArgs->getLastArg(OPT_machine)) { + StringRef arg = inputArg->getValue(); + llvm::COFF::MachineTypes type = stringToMachineType(arg); + if (type == llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN) { + diag << "error: unknown machine type: " << arg << "\n"; + return false; + } + ctx.setMachineType(type); + } else { + // If /machine option is missing, we need to take a look at + // the magic byte of the first object file to infer machine type. + std::vector<StringRef> filePaths; + for (auto arg : *parsedArgs) + if (arg->getOption().getID() == OPT_INPUT) + filePaths.push_back(arg->getValue()); + if (llvm::opt::Arg *arg = parsedArgs->getLastArg(OPT_DASH_DASH)) + filePaths.insert(filePaths.end(), arg->getValues().begin(), + arg->getValues().end()); + for (StringRef path : filePaths) { + llvm::COFF::MachineTypes type; + if (!getMachineType(path, type)) + continue; + if (type == llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN) + continue; + ctx.setMachineType(type); + break; + } + } + + // Handle /nodefaultlib:<lib>. The same option without argument is handled in + // the following for loop. + for (auto *arg : parsedArgs->filtered(OPT_nodefaultlib)) + ctx.addNoDefaultLib(arg->getValue()); + + // Handle /defaultlib. Argument of the option is added to the input file list + // unless it's blacklisted by /nodefaultlib. + std::vector<StringRef> defaultLibs; + for (auto *arg : parsedArgs->filtered(OPT_defaultlib)) + defaultLibs.push_back(arg->getValue()); + + // -alternatename:<alias>=<symbol> + for (auto *arg : parsedArgs->filtered(OPT_alternatename)) { + StringRef weak, def; + if (!parseAlternateName(arg->getValue(), weak, def, diag)) + return false; + ctx.addAlternateName(weak, def); + } + + // Parse /base command line option. The argument for the parameter is in + // the form of "<address>[:<size>]". + if (auto *arg = parsedArgs->getLastArg(OPT_base)) { + uint64_t addr, size; + // Size should be set to SizeOfImage field in the COFF header, and if + // it's smaller than the actual size, the linker should warn about that. + // Currently we just ignore the value of size parameter. + if (!parseMemoryOption(arg->getValue(), addr, size)) + return false; + ctx.setBaseAddress(addr); + } + + // Parse /dll command line option + if (parsedArgs->hasArg(OPT_dll)) { + ctx.setIsDll(true); + // Default base address of a DLL is 0x10000000. + if (!parsedArgs->hasArg(OPT_base)) + ctx.setBaseAddress(0x10000000); + } + + // Parse /stack command line option + if (auto *arg = parsedArgs->getLastArg(OPT_stack)) { + uint64_t reserve; + uint64_t commit = ctx.getStackCommit(); + if (!parseMemoryOption(arg->getValue(), reserve, commit)) + return false; + ctx.setStackReserve(reserve); + ctx.setStackCommit(commit); + } + + // Parse /heap command line option + if (auto *arg = parsedArgs->getLastArg(OPT_heap)) { + uint64_t reserve; + uint64_t commit = ctx.getHeapCommit(); + if (!parseMemoryOption(arg->getValue(), reserve, commit)) + return false; + ctx.setHeapReserve(reserve); + ctx.setHeapCommit(commit); + } + + if (auto *arg = parsedArgs->getLastArg(OPT_align)) { + uint32_t align; + StringRef val = arg->getValue(); + if (val.getAsInteger(10, align)) { + diag << "error: invalid value for /align: " << val << "\n"; + return false; + } + ctx.setSectionDefaultAlignment(align); + } + + if (auto *arg = parsedArgs->getLastArg(OPT_version)) { + uint32_t major, minor; + if (!parseVersion(arg->getValue(), major, minor)) + return false; + ctx.setImageVersion(PECOFFLinkingContext::Version(major, minor)); + } + + // Parse /merge:<from>=<to>. + for (auto *arg : parsedArgs->filtered(OPT_merge)) { + StringRef from, to; + std::tie(from, to) = StringRef(arg->getValue()).split('='); + if (from.empty() || to.empty()) { + diag << "error: malformed /merge option: " << arg->getValue() << "\n"; + return false; + } + if (!ctx.addSectionRenaming(diag, from, to)) + return false; + } + + // Parse /subsystem:<subsystem>[,<majorOSVersion>[.<minorOSVersion>]]. + if (auto *arg = parsedArgs->getLastArg(OPT_subsystem)) { + llvm::COFF::WindowsSubsystem subsystem; + llvm::Optional<uint32_t> major, minor; + if (!parseSubsystem(arg->getValue(), subsystem, major, minor, diag)) + return false; + ctx.setSubsystem(subsystem); + if (major.hasValue()) + ctx.setMinOSVersion(PECOFFLinkingContext::Version(*major, *minor)); + } + + // Parse /section:name,[[!]{DEKPRSW}] + for (auto *arg : parsedArgs->filtered(OPT_section)) { + std::string section; + llvm::Optional<uint32_t> flags, mask; + if (!parseSection(arg->getValue(), section, flags, mask)) { + diag << "Unknown argument for /section: " << arg->getValue() << "\n"; + return false; + } + if (flags.hasValue()) + ctx.setSectionSetMask(section, *flags); + if (mask.hasValue()) + ctx.setSectionClearMask(section, *mask); + } + + // Parse /manifest:EMBED[,ID=#]|NO. + if (auto *arg = parsedArgs->getLastArg(OPT_manifest_colon)) { + bool enable = true; + bool embed = false; + int id = 1; + if (!parseManifest(arg->getValue(), enable, embed, id)) { + diag << "Unknown argument for /manifest: " << arg->getValue() << "\n"; + return false; + } + ctx.setCreateManifest(enable); + ctx.setEmbedManifest(embed); + ctx.setManifestId(id); + } + + // Parse /manifestuac. + if (auto *arg = parsedArgs->getLastArg(OPT_manifestuac)) { + if (StringRef(arg->getValue()).equals_lower("no")) { + ctx.setManifestUAC(false); + } else { + llvm::Optional<std::string> privilegeLevel; + llvm::Optional<std::string> uiAccess; + if (!parseManifestUAC(arg->getValue(), privilegeLevel, uiAccess)) { + diag << "Unknown argument for /manifestuac: " << arg->getValue() + << "\n"; + return false; + } + if (privilegeLevel.hasValue()) + ctx.setManifestLevel(privilegeLevel.getValue()); + if (uiAccess.hasValue()) + ctx.setManifestUiAccess(uiAccess.getValue()); + } + } + + if (auto *arg = parsedArgs->getLastArg(OPT_manifestfile)) + ctx.setManifestOutputPath(ctx.allocate(arg->getValue())); + + // /manifestdependency:<string> option. Note that the argument will be + // embedded to the manifest XML file with no error check, for link.exe + // compatibility. We do not gurantete that the resulting XML file is + // valid. + if (auto *arg = parsedArgs->getLastArg(OPT_manifestdependency)) + ctx.setManifestDependency(ctx.allocate(arg->getValue())); + + for (auto *arg : parsedArgs->filtered(OPT_failifmismatch)) + if (handleFailIfMismatchOption(arg->getValue(), failIfMismatchMap, diag)) + return false; + + if (auto *arg = parsedArgs->getLastArg(OPT_entry)) + ctx.setEntrySymbolName(ctx.allocate(arg->getValue())); + + for (auto *arg : parsedArgs->filtered(OPT_export)) { + PECOFFLinkingContext::ExportDesc desc; + if (!parseExport(arg->getValue(), desc)) { + diag << "Error: malformed /export option: " << arg->getValue() << "\n"; + return false; + } + + // Mangle the symbol name only if it is reading user-supplied command line + // arguments. Because the symbol name in the .drectve section is already + // mangled by the compiler, we shouldn't add a leading underscore in that + // case. It's odd that the command line option has different semantics in + // the .drectve section, but this behavior is needed for compatibility + // with MSVC's link.exe. + if (!isReadingDirectiveSection) + desc.name = ctx.decorateSymbol(desc.name); + ctx.addDllExport(desc); + } + + for (auto *arg : parsedArgs->filtered(OPT_deffile)) { + llvm::BumpPtrAllocator alloc; + std::vector<moduledef::Directive *> dirs; + if (!parseDef(arg->getValue(), alloc, dirs)) { + diag << "Error: invalid module-definition file\n"; + return false; + } + for (moduledef::Directive *dir : dirs) { + if (auto *exp = dyn_cast<moduledef::Exports>(dir)) { + for (PECOFFLinkingContext::ExportDesc desc : exp->getExports()) { + desc.name = ctx.decorateSymbol(desc.name); + ctx.addDllExport(desc); + } + } else if (auto *hs = dyn_cast<moduledef::Heapsize>(dir)) { + ctx.setHeapReserve(hs->getReserve()); + ctx.setHeapCommit(hs->getCommit()); + } else if (auto *lib = dyn_cast<moduledef::Library>(dir)) { + ctx.setIsDll(true); + ctx.setOutputPath(ctx.allocate(lib->getName())); + if (lib->getBaseAddress() && !ctx.getBaseAddress()) + ctx.setBaseAddress(lib->getBaseAddress()); + } else if (auto *name = dyn_cast<moduledef::Name>(dir)) { + if (!name->getOutputPath().empty() && ctx.outputPath().empty()) + ctx.setOutputPath(ctx.allocate(name->getOutputPath())); + if (name->getBaseAddress() && ctx.getBaseAddress()) + ctx.setBaseAddress(name->getBaseAddress()); + } else if (auto *ver = dyn_cast<moduledef::Version>(dir)) { + ctx.setImageVersion(PECOFFLinkingContext::Version( + ver->getMajorVersion(), ver->getMinorVersion())); + } else { + llvm::dbgs() << static_cast<int>(dir->getKind()) << "\n"; + llvm_unreachable("Unknown module-definition directive.\n"); + } + } + } + + for (auto *arg : parsedArgs->filtered(OPT_libpath)) + ctx.appendInputSearchPath(ctx.allocate(arg->getValue())); + + for (auto *arg : parsedArgs->filtered(OPT_opt)) { + std::string val = StringRef(arg->getValue()).lower(); + if (val == "noref") { + ctx.setDeadStripping(false); + } else if (val != "ref" && val != "icf" && val != "noicf" && + val != "lbr" && val != "nolbr" && + !StringRef(val).startswith("icf=")) { + diag << "unknown option for /opt: " << val << "\n"; + return false; + } + } + + // LLD is not yet capable of creating a PDB file, so /debug does not have + // any effect. + // TODO: This should disable dead stripping. Currently we can't do that + // because removal of associative sections depends on dead stripping. + if (parsedArgs->hasArg(OPT_debug)) + ctx.setDebug(true); + + if (parsedArgs->hasArg(OPT_verbose)) + ctx.setLogInputFiles(true); + + // /force and /force:unresolved mean the same thing. We do not currently + // support /force:multiple. + if (parsedArgs->hasArg(OPT_force) || + parsedArgs->hasArg(OPT_force_unresolved)) { + ctx.setAllowRemainingUndefines(true); + } + + if (parsedArgs->hasArg(OPT_fixed)) { + // /fixed is not compatible with /dynamicbase. Check for it. + if (parsedArgs->hasArg(OPT_dynamicbase)) { + diag << "/dynamicbase must not be specified with /fixed\n"; + return false; + } + ctx.setBaseRelocationEnabled(false); + ctx.setDynamicBaseEnabled(false); + } + + // /swaprun:{cd,net} options set IMAGE_FILE_{REMOVABLE,NET}_RUN_FROM_SWAP + // bits in the COFF header, respectively. If one of the bits is on, the + // Windows loader will copy the entire file to swap area then execute it, + // so that the user can eject a CD or disconnect from the network. + if (parsedArgs->hasArg(OPT_swaprun_cd)) + ctx.setSwapRunFromCD(true); + + if (parsedArgs->hasArg(OPT_swaprun_net)) + ctx.setSwapRunFromNet(true); + + if (parsedArgs->hasArg(OPT_profile)) { + // /profile implies /opt:ref, /opt:noicf, /incremental:no and /fixed:no. + ctx.setDeadStripping(true); + ctx.setBaseRelocationEnabled(true); + ctx.setDynamicBaseEnabled(true); + } + + for (auto *arg : parsedArgs->filtered(OPT_implib)) + ctx.setOutputImportLibraryPath(arg->getValue()); + + for (auto *arg : parsedArgs->filtered(OPT_delayload)) { + ctx.addInitialUndefinedSymbol(ctx.getDelayLoadHelperName()); + ctx.addDelayLoadDLL(arg->getValue()); + } + + if (auto *arg = parsedArgs->getLastArg(OPT_stub)) { + ArrayRef<uint8_t> contents; + if (!readFile(ctx, arg->getValue(), contents)) { + diag << "Failed to read DOS stub file " << arg->getValue() << "\n"; + return false; + } + ctx.setDosStub(contents); + } + + for (auto *arg : parsedArgs->filtered(OPT_incl)) + ctx.addInitialUndefinedSymbol(ctx.allocate(arg->getValue())); + + if (parsedArgs->hasArg(OPT_noentry)) + ctx.setHasEntry(false); + + if (parsedArgs->hasArg(OPT_nodefaultlib_all)) + ctx.setNoDefaultLibAll(true); + + if (auto *arg = parsedArgs->getLastArg(OPT_out)) + ctx.setOutputPath(ctx.allocate(arg->getValue())); + + if (auto *arg = parsedArgs->getLastArg(OPT_pdb)) + ctx.setPDBFilePath(arg->getValue()); + + if (auto *arg = parsedArgs->getLastArg(OPT_lldmoduledeffile)) + ctx.setModuleDefinitionFile(arg->getValue()); + + std::vector<StringRef> inputFiles; + for (auto *arg : parsedArgs->filtered(OPT_INPUT)) + inputFiles.push_back(ctx.allocate(arg->getValue())); + +#define BOOLEAN_FLAG(name, setter) \ + if (auto *arg = parsedArgs->getLastArg(OPT_##name, OPT_##name##_no)) \ + ctx.setter(arg->getOption().matches(OPT_##name)); + + BOOLEAN_FLAG(nxcompat, setNxCompat); + BOOLEAN_FLAG(largeaddressaware, setLargeAddressAware); + BOOLEAN_FLAG(allowbind, setAllowBind); + BOOLEAN_FLAG(allowisolation, setAllowIsolation); + BOOLEAN_FLAG(dynamicbase, setDynamicBaseEnabled); + BOOLEAN_FLAG(tsaware, setTerminalServerAware); + BOOLEAN_FLAG(highentropyva, setHighEntropyVA); + BOOLEAN_FLAG(safeseh, setSafeSEH); +#undef BOOLEAN_FLAG + + // Arguments after "--" are interpreted as filenames even if they + // start with a hypen or a slash. This is not compatible with link.exe + // but useful for us to test lld on Unix. + if (llvm::opt::Arg *dashdash = parsedArgs->getLastArg(OPT_DASH_DASH)) + for (const StringRef value : dashdash->getValues()) + inputFiles.push_back(value); + + // Compile Windows resource files to compiled resource file. + if (ctx.getCreateManifest() && ctx.getEmbedManifest() && + !isReadingDirectiveSection) { + std::string resFile; + if (!createManifestResourceFile(ctx, diag, resFile)) + return false; + inputFiles.push_back(ctx.allocate(resFile)); + } + + // A Windows Resource file is not an object file. It contains data, + // such as an icon image, and is not in COFF file format. If resource + // files are given, the linker merge them into one COFF file using + // CVTRES.EXE and then link the resulting file. + { + auto it = std::partition(inputFiles.begin(), inputFiles.end(), + isResoruceFile); + if (it != inputFiles.begin()) { + std::vector<std::string> resFiles(inputFiles.begin(), it); + std::string resObj; + if (!convertResourceFiles(ctx, resFiles, resObj)) { + diag << "Failed to convert resource files\n"; + return false; + } + inputFiles = std::vector<StringRef>(it, inputFiles.end()); + inputFiles.push_back(ctx.allocate(resObj)); + ctx.registerTemporaryFile(resObj); + } + } + + // Prepare objects to add them to the list of input files. + for (StringRef path : inputFiles) { + path = ctx.allocate(path); + if (isLibraryFile(path)) { + addFiles(ctx, getLibraryPath(ctx, path), diag, libraries); + } else { + addFiles(ctx, getObjectPath(ctx, path), diag, files); + } + } + + // If dead-stripping is enabled, we need to add the entry symbol and + // symbols given by /include to the dead strip root set, so that it + // won't be removed from the output. + if (ctx.deadStrip()) + for (const StringRef symbolName : ctx.initialUndefinedSymbols()) + ctx.addDeadStripRoot(symbolName); + + // Add the libraries specified by /defaultlib unless they are already added + // nor blacklisted by /nodefaultlib. + if (!ctx.getNoDefaultLibAll()) + for (const StringRef path : defaultLibs) + if (!ctx.hasNoDefaultLib(path)) + addFiles(ctx, getLibraryPath(ctx, path.lower()), diag, libraries); + + if (files.empty() && !isReadingDirectiveSection) { + diag << "No input files\n"; + return false; + } + + // If /out option was not specified, the default output file name is + // constructed by replacing an extension of the first input file + // with ".exe". + if (ctx.outputPath().empty()) { + StringRef path = files[0]->path(); + ctx.setOutputPath(replaceExtension(ctx, path, ".exe")); + } + + // Add the input files to the linking context. + for (std::unique_ptr<File> &file : files) { + if (isReadingDirectiveSection) { + File *f = file.get(); + ctx.getTaskGroup().spawn([f] { f->parse(); }); + } + ctx.getNodes().push_back(llvm::make_unique<FileNode>(std::move(file))); + } + + // Add the library group to the linking context. + if (!isReadingDirectiveSection) { + // Add a group-end marker. + ctx.getNodes().push_back(llvm::make_unique<GroupEnd>(0)); + } + + // Add the library files to the library group. + for (std::unique_ptr<File> &file : libraries) { + if (!hasLibrary(ctx, file.get())) { + if (isReadingDirectiveSection) { + File *f = file.get(); + ctx.getTaskGroup().spawn([f] { f->parse(); }); + } + ctx.addLibraryFile(llvm::make_unique<FileNode>(std::move(file))); + } + } + + // Validate the combination of options used. + return ctx.validate(diag); +} + +} // namespace lld diff --git a/lib/Driver/WinLinkModuleDef.cpp b/lib/Driver/WinLinkModuleDef.cpp new file mode 100644 index 0000000000000..e55a0bc5fe649 --- /dev/null +++ b/lib/Driver/WinLinkModuleDef.cpp @@ -0,0 +1,295 @@ +//===- lib/Driver/WinLinkModuleDef.cpp ------------------------------------===// +// +// 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. +/// +//===----------------------------------------------------------------------===// + +#include "lld/Driver/WinLinkModuleDef.h" +#include "llvm/ADT/StringSwitch.h" + +namespace lld { +namespace moduledef { + +Token Lexer::lex() { + for (;;) { + _buffer = _buffer.trim(); + if (_buffer.empty() || _buffer[0] == '\0') + return Token(Kind::eof, _buffer); + + switch (_buffer[0]) { + case ';': { + size_t end = _buffer.find('\n'); + _buffer = (end == _buffer.npos) ? "" : _buffer.drop_front(end); + continue; + } + case '=': + _buffer = _buffer.drop_front(); + return Token(Kind::equal, "="); + case ',': + _buffer = _buffer.drop_front(); + return Token(Kind::comma, ","); + case '"': { + size_t end = _buffer.find('"', 1); + Token ret; + if (end == _buffer.npos) { + ret = Token(Kind::identifier, _buffer.substr(1, end)); + _buffer = ""; + } else { + ret = Token(Kind::identifier, _buffer.substr(1, end - 1)); + _buffer = _buffer.drop_front(end + 1); + } + return ret; + } + default: { + size_t end = _buffer.find_first_not_of( + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789_.*~+!@#$%^&*()/"); + StringRef word = _buffer.substr(0, end); + Kind kind = llvm::StringSwitch<Kind>(word) + .Case("BASE", Kind::kw_base) + .Case("DATA", Kind::kw_data) + .Case("EXPORTS", Kind::kw_exports) + .Case("HEAPSIZE", Kind::kw_heapsize) + .Case("LIBRARY", Kind::kw_library) + .Case("NAME", Kind::kw_name) + .Case("NONAME", Kind::kw_noname) + .Case("PRIVATE", Kind::kw_private) + .Case("STACKSIZE", Kind::kw_stacksize) + .Case("VERSION", Kind::kw_version) + .Default(Kind::identifier); + _buffer = (end == _buffer.npos) ? "" : _buffer.drop_front(end); + return Token(kind, word); + } + } + } +} + +void Parser::consumeToken() { + if (_tokBuf.empty()) { + _tok = _lex.lex(); + return; + } + _tok = _tokBuf.back(); + _tokBuf.pop_back(); +} + +bool Parser::consumeTokenAsInt(uint64_t &result) { + consumeToken(); + if (_tok._kind != Kind::identifier) { + ungetToken(); + error(_tok, "Integer expected"); + return false; + } + if (_tok._range.getAsInteger(10, result)) { + error(_tok, "Integer expected"); + return false; + } + return true; +} + +bool Parser::expectAndConsume(Kind kind, Twine msg) { + consumeToken(); + if (_tok._kind != kind) { + error(_tok, msg); + return false; + } + return true; +} + +void Parser::ungetToken() { _tokBuf.push_back(_tok); } + +void Parser::error(const Token &tok, Twine msg) { + _lex.getSourceMgr().PrintMessage( + llvm::SMLoc::getFromPointer(tok._range.data()), llvm::SourceMgr::DK_Error, + msg); +} + +bool Parser::parse(std::vector<Directive *> &ret) { + for (;;) { + Directive *dir = nullptr; + if (!parseOne(dir)) + return false; + if (!dir) + return true; + ret.push_back(dir); + } +} + +bool Parser::parseOne(Directive *&ret) { + consumeToken(); + switch (_tok._kind) { + case Kind::eof: + return true; + case Kind::kw_exports: { + // EXPORTS + std::vector<PECOFFLinkingContext::ExportDesc> exports; + for (;;) { + PECOFFLinkingContext::ExportDesc desc; + if (!parseExport(desc)) + break; + exports.push_back(desc); + } + ret = new (_alloc) Exports(exports); + return true; + } + case Kind::kw_heapsize: { + // HEAPSIZE + uint64_t reserve, commit; + if (!parseMemorySize(reserve, commit)) + return false; + ret = new (_alloc) Heapsize(reserve, commit); + return true; + } + case Kind::kw_library: { + // LIBRARY + std::string name; + uint64_t baseaddr; + if (!parseName(name, baseaddr)) + return false; + if (!StringRef(name).endswith_lower(".dll")) + name.append(".dll"); + ret = new (_alloc) Library(name, baseaddr); + return true; + } + case Kind::kw_stacksize: { + // STACKSIZE + uint64_t reserve, commit; + if (!parseMemorySize(reserve, commit)) + return false; + ret = new (_alloc) Stacksize(reserve, commit); + return true; + } + case Kind::kw_name: { + // NAME + std::string outputPath; + uint64_t baseaddr; + if (!parseName(outputPath, baseaddr)) + return false; + ret = new (_alloc) Name(outputPath, baseaddr); + return true; + } + case Kind::kw_version: { + // VERSION + int major, minor; + if (!parseVersion(major, minor)) + return false; + ret = new (_alloc) Version(major, minor); + return true; + } + default: + error(_tok, Twine("Unknown directive: ") + _tok._range); + return false; + } +} + +bool Parser::parseExport(PECOFFLinkingContext::ExportDesc &result) { + consumeToken(); + if (_tok._kind != Kind::identifier) { + ungetToken(); + return false; + } + result.name = _tok._range; + + consumeToken(); + if (_tok._kind == Kind::equal) { + consumeToken(); + if (_tok._kind != Kind::identifier) + return false; + result.externalName = result.name; + result.name = _tok._range; + } else { + ungetToken(); + } + + for (;;) { + consumeToken(); + if (_tok._kind == Kind::identifier && _tok._range[0] == '@') { + _tok._range.drop_front().getAsInteger(10, result.ordinal); + consumeToken(); + if (_tok._kind == Kind::kw_noname) { + result.noname = true; + } else { + ungetToken(); + } + continue; + } + if (_tok._kind == Kind::kw_data) { + result.isData = true; + continue; + } + if (_tok._kind == Kind::kw_private) { + result.isPrivate = true; + continue; + } + ungetToken(); + return true; + } +} + +// HEAPSIZE/STACKSIZE reserve[,commit] +bool Parser::parseMemorySize(uint64_t &reserve, uint64_t &commit) { + if (!consumeTokenAsInt(reserve)) + return false; + + consumeToken(); + if (_tok._kind != Kind::comma) { + ungetToken(); + commit = 0; + return true; + } + + if (!consumeTokenAsInt(commit)) + return false; + return true; +} + +// NAME [outputPath] [BASE=address] +bool Parser::parseName(std::string &outputPath, uint64_t &baseaddr) { + consumeToken(); + if (_tok._kind == Kind::identifier) { + outputPath = _tok._range; + } else { + outputPath = ""; + ungetToken(); + return true; + } + consumeToken(); + if (_tok._kind == Kind::kw_base) { + if (!expectAndConsume(Kind::equal, "'=' expected")) + return false; + if (!consumeTokenAsInt(baseaddr)) + return false; + } else { + ungetToken(); + baseaddr = 0; + } + return true; +} + +// VERSION major[.minor] +bool Parser::parseVersion(int &major, int &minor) { + consumeToken(); + if (_tok._kind != Kind::identifier) + return false; + StringRef v1, v2; + std::tie(v1, v2) = _tok._range.split('.'); + if (v1.getAsInteger(10, major)) + return false; + if (v2.empty()) { + minor = 0; + } else if (v2.getAsInteger(10, minor)) { + return false; + } + return true; +} + +} // moddef +} // namespace lld diff --git a/lib/Driver/WinLinkOptions.td b/lib/Driver/WinLinkOptions.td new file mode 100644 index 0000000000000..a545639b5bb27 --- /dev/null +++ b/lib/Driver/WinLinkOptions.td @@ -0,0 +1,120 @@ +include "llvm/Option/OptParser.td" + +// link.exe accepts options starting with either a dash or a slash. + +// Flag that takes no arguments. +class F<string name> : Flag<["/", "-", "-?"], name>; + +// Flag that takes one argument after ":". +class P<string name, string help> : + Joined<["/", "-", "-?"], name#":">, HelpText<help>; + +// Boolean flag suffixed by ":no". +multiclass B<string name, string help> { + def "" : F<name>; + def _no : F<name#":no">, HelpText<help>; +} + +def alternatename : P<"alternatename", "Define weak alias">; +def base : P<"base", "Base address of the program">; +def defaultlib : P<"defaultlib", "Add the library to the list of input files">; +def nodefaultlib : P<"nodefaultlib", "Remove a default library">; +def disallowlib : Joined<["/", "-", "-?"], "disallowlib:">, Alias<nodefaultlib>; +def entry : P<"entry", "Name of entry point symbol">; +// No help text because /failifmismatch is not intended to be used by the user. +def export : P<"export", "Export a function">; +def failifmismatch : P<"failifmismatch", "">; +def heap : P<"heap", "Size of the heap">; +def align : P<"align", "Section alignment">; +def libpath : P<"libpath", "Additional library search path">; +def mllvm : P<"mllvm", "Options to pass to LLVM">; +def out : P<"out", "Path to file to write output">; +def stack : P<"stack", "Size of the stack">; +def machine : P<"machine", "Specify target platform">; +def version : P<"version", "Specify a version number in the PE header">; +def merge : P<"merge", "Combine sections">; +def section : P<"section", "Specify section attributes">; +def subsystem : P<"subsystem", "Specify subsystem">; +def stub : P<"stub", "Specify DOS stub file">; +def opt : P<"opt", "Control optimizations">; +def implib : P<"implib", "Import library name">; +def delayload : P<"delayload", "Delay loaded DLL name">; +def pdb : P<"pdb", "PDB file path">; + +def manifest : F<"manifest">; +def manifest_colon : P<"manifest", "Create manifest file">; +def manifestuac : P<"manifestuac", "User access control">; +def manifestfile : P<"manifestfile", "Manifest file path">; +def manifestdependency : P<"manifestdependency", + "Attributes for <dependency> in manifest file">; + +// We cannot use multiclass P because class name "incl" is different +// from its command line option name. We do this because "include" is +// a reserved keyword in tablegen. +def incl : Joined<["/", "-"], "include:">, + HelpText<"Force symbol to be added to symbol table as undefined one">; + +// "def" is also a keyword. +def deffile : Joined<["/", "-"], "def:">, + HelpText<"Use module-definition file">; + +def nodefaultlib_all : F<"nodefaultlib">; +def noentry : F<"noentry">; +def dll : F<"dll">; +def verbose : F<"verbose">; +def debug : F<"debug">; +def swaprun_cd : F<"swaprun:cd">; +def swaprun_net : F<"swaprun:net">; +def profile : F<"profile">; + +def force : F<"force">, + HelpText<"Allow undefined symbols when creating executables">; +def force_unresolved : F<"force:unresolved">; + +defm nxcompat : B<"nxcompat", "Disable data execution provention">; +defm largeaddressaware : B<"largeaddressaware", "Disable large addresses">; +defm allowbind: B<"allowbind", "Disable DLL binding">; +defm fixed : B<"fixed", "Enable base relocations">; +defm tsaware : B<"tsaware", "Create non-Terminal Server aware executable">; +defm allowisolation : B<"allowisolation", "Set NO_ISOLATION bit">; +defm dynamicbase : B<"dynamicbase", + "Disable address space layout randomization">; +defm safeseh : B<"safeseh", "Produce an image with Safe Exception Handler">; +defm highentropyva : B<"highentropyva", "Set HIGH_ENTROPY_VA bit">; + +def help : F<"help">; +def help_q : Flag<["/?", "-?"], "">, Alias<help>; + +def DASH_DASH : Option<["--"], "", KIND_REMAINING_ARGS>; + +// Flag for debug +def lldmoduledeffile : Joined<["/", "-"], "lldmoduledeffile:">; + +//============================================================================== +// The flags below do nothing. They are defined only for link.exe compatibility. +//============================================================================== + +class QF<string name> : Joined<["/", "-", "-?"], name#":">; + +multiclass QB<string name> { + def "" : F<name>; + def _no : F<name#":no">; +} + +def functionpadmin : F<"functionpadmin">; +def ignoreidl : F<"ignoreidl">; +def incremental : F<"incremental">; +def no_incremental : F<"incremental:no">; +def nologo : F<"nologo">; + +def delay : QF<"delay">; +def errorreport : QF<"errorreport">; +def idlout : QF<"idlout">; +def ignore : QF<"ignore">; +def maxilksize : QF<"maxilksize">; +def pdbaltpath : QF<"pdbaltpath">; +def tlbid : QF<"tlbid">; +def tlbout : QF<"tlbout">; +def verbose_all : QF<"verbose">; + +defm wx : QB<"wx">; diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 0000000000000..83112eaf972ae --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,16 @@ +##===- lib/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 := .. + +# ARCMigrate and Rewrite are always needed because of libclang. +PARALLEL_DIRS = Config Core Driver ReaderWriter + +include $(LLD_LEVEL)/../../Makefile.config + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/CMakeLists.txt b/lib/ReaderWriter/CMakeLists.txt new file mode 100644 index 0000000000000..1fd19eb73a755 --- /dev/null +++ b/lib/ReaderWriter/CMakeLists.txt @@ -0,0 +1,20 @@ +add_subdirectory(ELF) +add_subdirectory(MachO) +add_subdirectory(Native) +add_subdirectory(PECOFF) +add_subdirectory(YAML) + +if (MSVC) + add_definitions(-wd4062) # Suppress 'warning C4062: Enumerator has no associated handler in a switch statement.' +endif() + +add_llvm_library(lldReaderWriter + CoreLinkingContext.cpp + FileArchive.cpp + LinkerScript.cpp + LINK_LIBS + lldCore + lldYAML + LLVMObject + LLVMSupport + ) diff --git a/lib/ReaderWriter/CoreLinkingContext.cpp b/lib/ReaderWriter/CoreLinkingContext.cpp new file mode 100644 index 0000000000000..86fad4f6e77d0 --- /dev/null +++ b/lib/ReaderWriter/CoreLinkingContext.cpp @@ -0,0 +1,171 @@ +//===- lib/ReaderWriter/CoreLinkingContext.cpp ----------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lld/Core/DefinedAtom.h" +#include "lld/Core/File.h" +#include "lld/Core/Pass.h" +#include "lld/Core/PassManager.h" +#include "lld/Core/Simple.h" +#include "lld/ReaderWriter/CoreLinkingContext.h" +#include "llvm/ADT/ArrayRef.h" + +using namespace lld; + +namespace { + +/// \brief Simple atom created by the stubs pass. +class TestingStubAtom : public DefinedAtom { +public: + TestingStubAtom(const File &F, const Atom &) : _file(F) { + static uint32_t lastOrdinal = 0; + _ordinal = lastOrdinal++; + } + + const File &file() const override { return _file; } + + StringRef name() const override { return StringRef(); } + + uint64_t ordinal() const override { return _ordinal; } + + uint64_t size() const override { return 0; } + + Scope scope() const override { return DefinedAtom::scopeLinkageUnit; } + + Interposable interposable() const override { return DefinedAtom::interposeNo; } + + Merge merge() const override { return DefinedAtom::mergeNo; } + + ContentType contentType() const override { return DefinedAtom::typeStub; } + + 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; + } + + ContentPermissions permissions() const override { + return DefinedAtom::permR_X; + } + + ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } + + reference_iterator begin() const override { + return reference_iterator(*this, nullptr); + } + + reference_iterator end() const override { + return reference_iterator(*this, nullptr); + } + + const Reference *derefIterator(const void *iter) const override { + return nullptr; + } + + void incrementIterator(const void *&iter) const override {} + +private: + const File &_file; + uint32_t _ordinal; +}; + +/// \brief Simple atom created by the GOT pass. +class TestingGOTAtom : public DefinedAtom { +public: + TestingGOTAtom(const File &F, const Atom &) : _file(F) { + static uint32_t lastOrdinal = 0; + _ordinal = lastOrdinal++; + } + + const File &file() const override { return _file; } + + StringRef name() const override { return StringRef(); } + + uint64_t ordinal() const override { return _ordinal; } + + uint64_t size() const override { return 0; } + + Scope scope() const override { return DefinedAtom::scopeLinkageUnit; } + + Interposable interposable() const override { return DefinedAtom::interposeNo; } + + Merge merge() const override { return DefinedAtom::mergeNo; } + + ContentType contentType() const override { return DefinedAtom::typeGOT; } + + Alignment alignment() const override { return Alignment(3, 0); } + + SectionChoice sectionChoice() const override { + return DefinedAtom::sectionBasedOnContent; + } + + StringRef customSectionName() const override { return StringRef(); } + + DeadStripKind deadStrip() const override { + return DefinedAtom::deadStripNormal; + } + + ContentPermissions permissions() const override { + return DefinedAtom::permRW_; + } + + ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } + + reference_iterator begin() const override { + return reference_iterator(*this, nullptr); + } + + reference_iterator end() const override { + return reference_iterator(*this, nullptr); + } + + const Reference *derefIterator(const void *iter) const override { + return nullptr; + } + + void incrementIterator(const void *&iter) const override {} + +private: + const File &_file; + uint32_t _ordinal; +}; + +class OrderPass : public Pass { +public: + /// Sorts atoms by position + void perform(std::unique_ptr<MutableFile> &file) override { + MutableFile::DefinedAtomRange defined = file->definedAtoms(); + std::sort(defined.begin(), defined.end(), DefinedAtom::compareByPosition); + } +}; + +} // anonymous namespace + +CoreLinkingContext::CoreLinkingContext() {} + +bool CoreLinkingContext::validateImpl(raw_ostream &) { + _writer = createWriterYAML(*this); + return true; +} + +void CoreLinkingContext::addPasses(PassManager &pm) { + for (StringRef name : _passNames) { + if (name.equals("order")) + pm.add(std::unique_ptr<Pass>(new OrderPass())); + else + llvm_unreachable("bad pass name"); + } +} + +Writer &CoreLinkingContext::writer() const { return *_writer; } diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64DynamicLibraryWriter.h b/lib/ReaderWriter/ELF/AArch64/AArch64DynamicLibraryWriter.h new file mode 100644 index 0000000000000..12ba52a38f380 --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64DynamicLibraryWriter.h @@ -0,0 +1,69 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64DynamicLibraryWriter.h ---------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef AARCH64_DYNAMIC_LIBRARY_WRITER_H +#define AARCH64_DYNAMIC_LIBRARY_WRITER_H + +#include "AArch64LinkingContext.h" +#include "AArch64TargetHandler.h" +#include "DynamicLibraryWriter.h" + +namespace lld { +namespace elf { + +template <class ELFT> +class AArch64DynamicLibraryWriter : public DynamicLibraryWriter<ELFT> { +public: + AArch64DynamicLibraryWriter(AArch64LinkingContext &context, + AArch64TargetLayout<ELFT> &layout); + +protected: + // Add any runtime files and their atoms to the output + virtual bool createImplicitFiles(std::vector<std::unique_ptr<File>> &); + + virtual void finalizeDefaultAtomValues() { + return DynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues(); + } + + virtual void addDefaultAtoms() { + return DynamicLibraryWriter<ELFT>::addDefaultAtoms(); + } + +private: + class GOTFile : public SimpleFile { + public: + GOTFile(const ELFLinkingContext &eti) : SimpleFile("GOTFile") {} + llvm::BumpPtrAllocator _alloc; + }; + + std::unique_ptr<GOTFile> _gotFile; + AArch64LinkingContext &_context; + AArch64TargetLayout<ELFT> &_AArch64Layout; +}; + +template <class ELFT> +AArch64DynamicLibraryWriter<ELFT>::AArch64DynamicLibraryWriter( + AArch64LinkingContext &context, AArch64TargetLayout<ELFT> &layout) + : DynamicLibraryWriter<ELFT>(context, layout), + _gotFile(new GOTFile(context)), _context(context), + _AArch64Layout(layout) {} + +template <class ELFT> +bool AArch64DynamicLibraryWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + DynamicLibraryWriter<ELFT>::createImplicitFiles(result); + _gotFile->addAtom(*new (_gotFile->_alloc) GLOBAL_OFFSET_TABLEAtom(*_gotFile)); + _gotFile->addAtom(*new (_gotFile->_alloc) DYNAMICAtom(*_gotFile)); + result.push_back(std::move(_gotFile)); + return true; +} + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64ELFFile.h b/lib/ReaderWriter/ELF/AArch64/AArch64ELFFile.h new file mode 100644 index 0000000000000..9d5207c1c4b49 --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64ELFFile.h @@ -0,0 +1,41 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64ELFFile.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_AARCH64_AARCH64_ELF_FILE_H +#define LLD_READER_WRITER_ELF_AARCH64_AARCH64_ELF_FILE_H + +#include "ELFReader.h" + +namespace lld { +namespace elf { + +class AArch64LinkingContext; + +template <class ELFT> class AArch64ELFFile : public ELFFile<ELFT> { +public: + AArch64ELFFile(std::unique_ptr<MemoryBuffer> mb, AArch64LinkingContext &ctx) + : ELFFile<ELFT>(std::move(mb), ctx) {} + + static ErrorOr<std::unique_ptr<AArch64ELFFile>> + create(std::unique_ptr<MemoryBuffer> mb, AArch64LinkingContext &ctx) { + return std::unique_ptr<AArch64ELFFile<ELFT>>( + new AArch64ELFFile<ELFT>(std::move(mb), ctx)); + } +}; + +template <class ELFT> class AArch64DynamicFile : public DynamicFile<ELFT> { +public: + AArch64DynamicFile(const AArch64LinkingContext &context, StringRef name) + : DynamicFile<ELFT>(context, name) {} +}; + +} // elf +} // lld + +#endif // LLD_READER_WRITER_ELF_AARCH64_AARCH64_ELF_FILE_H diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64ELFReader.h b/lib/ReaderWriter/ELF/AArch64/AArch64ELFReader.h new file mode 100644 index 0000000000000..05f312db3e7b7 --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64ELFReader.h @@ -0,0 +1,62 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64ELFReader.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_AARCH64_AARCH64_ELF_READER_H +#define LLD_READER_WRITER_AARCH64_AARCH64_ELF_READER_H + +#include "AArch64ELFFile.h" +#include "ELFReader.h" + +namespace lld { +namespace elf { + +typedef llvm::object::ELFType<llvm::support::little, 2, true> AArch64ELFType; + +struct AArch64DynamicFileCreateELFTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::SharedLibraryFile>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + AArch64LinkingContext &ctx) { + return lld::elf::AArch64DynamicFile<ELFT>::create(std::move(mb), ctx); + } +}; + +struct AArch64ELFFileCreateELFTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::File>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + AArch64LinkingContext &ctx) { + return lld::elf::AArch64ELFFile<ELFT>::create(std::move(mb), ctx); + } +}; + +class AArch64ELFObjectReader + : public ELFObjectReader<AArch64ELFType, AArch64ELFFileCreateELFTraits, + AArch64LinkingContext> { +public: + AArch64ELFObjectReader(AArch64LinkingContext &ctx) + : ELFObjectReader<AArch64ELFType, AArch64ELFFileCreateELFTraits, + AArch64LinkingContext>(ctx, llvm::ELF::EM_AARCH64) {} +}; + +class AArch64ELFDSOReader + : public ELFDSOReader<AArch64ELFType, AArch64DynamicFileCreateELFTraits, + AArch64LinkingContext> { +public: + AArch64ELFDSOReader(AArch64LinkingContext &ctx) + : ELFDSOReader<AArch64ELFType, AArch64DynamicFileCreateELFTraits, + AArch64LinkingContext>(ctx, llvm::ELF::EM_AARCH64) {} +}; + +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_AARCH64_AARCH64_ELF_READER_H diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.h b/lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.h new file mode 100644 index 0000000000000..73963f56ef70f --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.h @@ -0,0 +1,68 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64ExecutableWriter.h -------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef AARCH64_EXECUTABLE_WRITER_H +#define AARCH64_EXECUTABLE_WRITER_H + +#include "AArch64LinkingContext.h" +#include "ExecutableWriter.h" + +namespace lld { +namespace elf { + +template <class ELFT> +class AArch64ExecutableWriter : public ExecutableWriter<ELFT> { +public: + AArch64ExecutableWriter(AArch64LinkingContext &context, + AArch64TargetLayout<ELFT> &layout); + +protected: + // Add any runtime files and their atoms to the output + bool createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; + + void finalizeDefaultAtomValues() override { + return ExecutableWriter<ELFT>::finalizeDefaultAtomValues(); + } + + void addDefaultAtoms() override{ + return ExecutableWriter<ELFT>::addDefaultAtoms(); + } + +private: + class GOTFile : public SimpleFile { + public: + GOTFile(const ELFLinkingContext &eti) : SimpleFile("GOTFile") {} + llvm::BumpPtrAllocator _alloc; + }; + + std::unique_ptr<GOTFile> _gotFile; + AArch64LinkingContext &_context; + AArch64TargetLayout<ELFT> &_AArch64Layout; +}; + +template <class ELFT> +AArch64ExecutableWriter<ELFT>::AArch64ExecutableWriter( + AArch64LinkingContext &context, AArch64TargetLayout<ELFT> &layout) + : ExecutableWriter<ELFT>(context, layout), _gotFile(new GOTFile(context)), + _context(context), _AArch64Layout(layout) {} + +template <class ELFT> +bool AArch64ExecutableWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + ExecutableWriter<ELFT>::createImplicitFiles(result); + _gotFile->addAtom(*new (_gotFile->_alloc) GLOBAL_OFFSET_TABLEAtom(*_gotFile)); + if (_context.isDynamic()) + _gotFile->addAtom(*new (_gotFile->_alloc) DYNAMICAtom(*_gotFile)); + result.push_back(std::move(_gotFile)); + return true; +} + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.cpp b/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.cpp new file mode 100644 index 0000000000000..9eb98f4477098 --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.cpp @@ -0,0 +1,33 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.cpp -------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AArch64LinkingContext.h" +#include "AArch64RelocationPass.h" +#include "AArch64TargetHandler.h" + +using namespace lld; + +std::unique_ptr<ELFLinkingContext> +elf::AArch64LinkingContext::create(llvm::Triple triple) { + if (triple.getArch() == llvm::Triple::aarch64) + return std::unique_ptr<ELFLinkingContext>( + new elf::AArch64LinkingContext(triple)); + return nullptr; +} + +elf::AArch64LinkingContext::AArch64LinkingContext(llvm::Triple triple) + : ELFLinkingContext(triple, std::unique_ptr<TargetHandlerBase>( + new AArch64TargetHandler(*this))) {} + +void elf::AArch64LinkingContext::addPasses(PassManager &pm) { + auto pass = createAArch64RelocationPass(*this); + if (pass) + pm.add(std::move(pass)); + ELFLinkingContext::addPasses(pm); +} diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.h b/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.h new file mode 100644 index 0000000000000..ebd91fe0a95b8 --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.h @@ -0,0 +1,95 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64LinkingContext.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_AARCH64_AARCH64_LINKING_CONTEXT_H +#define LLD_READER_WRITER_ELF_AARCH64_AARCH64_LINKING_CONTEXT_H + +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/ELF.h" + +namespace lld { +namespace elf { + +enum { + /// \brief The offset to add operation for a R_AARCH64_ADR_GOT_PAGE + ADD_AARCH64_GOTRELINDEX = 0xE000, +}; + +class AArch64LinkingContext final : public ELFLinkingContext { +public: + static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + AArch64LinkingContext(llvm::Triple); + + void addPasses(PassManager &) override; + + uint64_t getBaseAddress() const override { + if (_baseAddress == 0) + return 0x400000; + return _baseAddress; + } + + bool isDynamicRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::AArch64); + switch (r.kindValue()) { + case llvm::ELF::R_AARCH64_COPY: + case llvm::ELF::R_AARCH64_GLOB_DAT: + case llvm::ELF::R_AARCH64_RELATIVE: + case llvm::ELF::R_AARCH64_TLS_DTPREL64: + case llvm::ELF::R_AARCH64_TLS_DTPMOD64: + case llvm::ELF::R_AARCH64_TLS_TPREL64: + case llvm::ELF::R_AARCH64_TLSDESC: + case llvm::ELF::R_AARCH64_IRELATIVE: + return true; + default: + return false; + } + } + + bool isCopyRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::AArch64); + if (r.kindValue() == llvm::ELF::R_AARCH64_COPY) + return true; + return false; + } + + bool isPLTRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::AArch64); + switch (r.kindValue()) { + case llvm::ELF::R_AARCH64_JUMP_SLOT: + case llvm::ELF::R_AARCH64_IRELATIVE: + return true; + default: + return false; + } + } + + bool isRelativeReloc(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::AArch64); + switch (r.kindValue()) { + case llvm::ELF::R_AARCH64_IRELATIVE: + case llvm::ELF::R_AARCH64_RELATIVE: + return true; + default: + return false; + } + } +}; +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.cpp b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.cpp new file mode 100644 index 0000000000000..d1ecc7fa884b0 --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.cpp @@ -0,0 +1,440 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.cpp ----------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AArch64TargetHandler.h" +#include "AArch64LinkingContext.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/MathExtras.h" + +using namespace lld; +using namespace lld::elf; +using namespace llvm::support::endian; + +#define PAGE(X) ((X) & ~0x0FFFL) + +/// \brief Check X is in the interval (-2^(bits-1), 2^bits] +static bool withinSignedUnsignedRange(int64_t X, int bits) { + return isIntN(bits - 1, X) || isUIntN(bits, X); +} + +/// \brief R_AARCH64_ABS64 - word64: S + A +static void relocR_AARCH64_ABS64(uint8_t *location, uint64_t P, uint64_t S, + int64_t A) { + int64_t result = (int64_t)S + A; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + write64le(location, result | read64le(location)); +} + +/// \brief R_AARCH64_PREL32 - word32: S + A - P +static void relocR_AARCH64_PREL32(uint8_t *location, uint64_t P, uint64_t S, + int64_t A) { + int32_t result = (int32_t)((S + A) - P); + write32le(location, result + (int32_t)read32le(location)); +} + +/// \brief R_AARCH64_ABS32 - word32: S + A +static std::error_code relocR_AARCH64_ABS32(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int64_t result = S + A; + if (!withinSignedUnsignedRange(result, 32)) + return make_out_of_range_reloc_error(); + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); + return std::error_code(); +} + +/// \brief R_AARCH64_ADR_PREL_PG_HI21 - Page(S+A) - Page(P) +static void relocR_AARCH64_ADR_PREL_PG_HI21(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + uint64_t result = (PAGE(S + A) - PAGE(P)); + result = result >> 12; + uint32_t immlo = result & 0x3; + uint32_t immhi = result & 0x1FFFFC; + immlo = immlo << 29; + immhi = immhi << 3; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " immhi: " << Twine::utohexstr(immhi); + llvm::dbgs() << " immlo: " << Twine::utohexstr(immlo); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, immlo | immhi | read32le(location)); + // TODO: Make sure this is correct! +} + +/// \brief R_AARCH64_ADR_PREL_LO21 - S + A - P +static void relocR_AARCH64_ADR_PREL_LO21(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + uint64_t result = (S + A) - P; + uint32_t immlo = result & 0x3; + uint32_t immhi = result & 0x1FFFFC; + immlo = immlo << 29; + immhi = immhi << 3; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " immhi: " << Twine::utohexstr(immhi); + llvm::dbgs() << " immlo: " << Twine::utohexstr(immlo); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, immlo | immhi | read32le(location)); + // TODO: Make sure this is correct! +} + +/// \brief R_AARCH64_ADD_ABS_LO12_NC +static void relocR_AARCH64_ADD_ABS_LO12_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = (int32_t)((S + A) & 0xFFF); + result <<= 10; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +static void relocJump26(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { + int32_t result = (int32_t)((S + A) - P); + result &= 0x0FFFFFFC; + result >>= 2; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +/// \brief R_AARCH64_CONDBR19 +static void relocR_AARCH64_CONDBR19(uint8_t *location, uint64_t P, uint64_t S, + int64_t A) { + int32_t result = (int32_t)((S + A) - P); + result &= 0x01FFFFC; + result <<= 3; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +/// \brief R_AARCH64_LDST8_ABS_LO12_NC - S + A +static void relocR_AARCH64_LDST8_ABS_LO12_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = (int32_t)((S + A) & 0xFFF); + result <<= 10; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +/// \brief R_AARCH64_LDST16_ABS_LO12_NC +static void relocR_AARCH64_LDST16_ABS_LO12_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = (int32_t)(S + A); + result &= 0x0FFC; + result <<= 9; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +/// \brief R_AARCH64_LDST32_ABS_LO12_NC +static void relocR_AARCH64_LDST32_ABS_LO12_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = (int32_t)(S + A); + result &= 0x0FFC; + result <<= 8; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +/// \brief R_AARCH64_LDST64_ABS_LO12_NC +static void relocR_AARCH64_LDST64_ABS_LO12_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = (int32_t)(S + A); + result &= 0x0FF8; + result <<= 7; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +/// \brief R_AARCH64_LDST128_ABS_LO12_NC +static void relocR_AARCH64_LDST128_ABS_LO12_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = (int32_t)(S + A); + result &= 0x0FF8; + result <<= 6; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +static void relocR_AARCH64_ADR_GOT_PAGE(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + uint64_t result = PAGE(S + A) - PAGE(P); + result >>= 12; + uint32_t immlo = result & 0x3; + uint32_t immhi = result & 0x1FFFFC; + immlo = immlo << 29; + immhi = immhi << 3; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " immhi: " << Twine::utohexstr(immhi); + llvm::dbgs() << " immlo: " << Twine::utohexstr(immlo); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +// R_AARCH64_LD64_GOT_LO12_NC +static void relocR_AARCH64_LD64_GOT_LO12_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = S + A; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + result &= 0xFF8; + result <<= 7; + write32le(location, result | read32le(location)); +} + +// ADD_AARCH64_GOTRELINDEX +static void relocADD_AARCH64_GOTRELINDEX(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = S + A; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + result &= 0xFFF; + result <<= 10; + write32le(location, result | read32le(location)); +} + +// R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 +static void relocR_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21(uint8_t *location, + uint64_t P, uint64_t S, + int64_t A) { + int64_t result = PAGE(S + A) - PAGE(P); + result >>= 12; + uint32_t immlo = result & 0x3; + uint32_t immhi = result & 0x1FFFFC; + immlo = immlo << 29; + immhi = immhi << 3; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " immhi: " << Twine::utohexstr(immhi); + llvm::dbgs() << " immlo: " << Twine::utohexstr(immlo); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, immlo | immhi | read32le(location)); +} + +// R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC +static void relocR_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC(uint8_t *location, + uint64_t P, uint64_t S, + int64_t A) { + int32_t result = S + A; + result &= 0xFF8; + result <<= 7; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +/// \brief R_AARCH64_TLSLE_ADD_TPREL_HI12 +static void relocR_AARCH64_TLSLE_ADD_TPREL_HI12(uint8_t *location, uint64_t P, + uint64_t S, int64_t A) { + int32_t result = S + A; + result &= 0x0FFF000; + result >>= 2; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +/// \brief R_AARCH64_TLSLE_ADD_TPREL_LO12_NC +static void relocR_AARCH64_TLSLE_ADD_TPREL_LO12_NC(uint8_t *location, + uint64_t P, uint64_t S, + int64_t A) { + int32_t result = S + A; + result &= 0x0FFF; + result <<= 10; + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: " << Twine::utohexstr(S); + llvm::dbgs() << " A: " << Twine::utohexstr(A); + llvm::dbgs() << " P: " << Twine::utohexstr(P); + llvm::dbgs() << " result: " << Twine::utohexstr(result) << "\n"); + write32le(location, result | read32le(location)); +} + +std::error_code AArch64TargetRelocationHandler::applyRelocation( + ELFWriter &writer, llvm::FileOutputBuffer &buf, const lld::AtomLayout &atom, + const Reference &ref) const { + uint8_t *atomContent = buf.getBufferStart() + atom._fileOffset; + uint8_t *location = atomContent + ref.offsetInAtom(); + uint64_t targetVAddress = writer.addressOfAtom(ref.target()); + uint64_t relocVAddress = atom._virtualAddr + ref.offsetInAtom(); + + if (ref.kindNamespace() != Reference::KindNamespace::ELF) + return std::error_code(); + assert(ref.kindArch() == Reference::KindArch::AArch64); + switch (ref.kindValue()) { + case R_AARCH64_NONE: + break; + case R_AARCH64_ABS64: + relocR_AARCH64_ABS64(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_AARCH64_PREL32: + relocR_AARCH64_PREL32(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_ABS32: + return relocR_AARCH64_ABS32(location, relocVAddress, targetVAddress, + ref.addend()); + // Runtime only relocations. Ignore here. + case R_AARCH64_RELATIVE: + case R_AARCH64_IRELATIVE: + case R_AARCH64_JUMP_SLOT: + case R_AARCH64_GLOB_DAT: + break; + case R_AARCH64_ADR_PREL_PG_HI21: + relocR_AARCH64_ADR_PREL_PG_HI21(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_ADR_PREL_LO21: + relocR_AARCH64_ADR_PREL_LO21(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_ADD_ABS_LO12_NC: + relocR_AARCH64_ADD_ABS_LO12_NC(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_CALL26: + case R_AARCH64_JUMP26: + relocJump26(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_AARCH64_CONDBR19: + relocR_AARCH64_CONDBR19(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_ADR_GOT_PAGE: + relocR_AARCH64_ADR_GOT_PAGE(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_LD64_GOT_LO12_NC: + relocR_AARCH64_LD64_GOT_LO12_NC(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_LDST8_ABS_LO12_NC: + relocR_AARCH64_LDST8_ABS_LO12_NC(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_LDST16_ABS_LO12_NC: + relocR_AARCH64_LDST16_ABS_LO12_NC(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_LDST32_ABS_LO12_NC: + relocR_AARCH64_LDST32_ABS_LO12_NC(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_LDST64_ABS_LO12_NC: + relocR_AARCH64_LDST64_ABS_LO12_NC(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_LDST128_ABS_LO12_NC: + relocR_AARCH64_LDST128_ABS_LO12_NC(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case ADD_AARCH64_GOTRELINDEX: + relocADD_AARCH64_GOTRELINDEX(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: + relocR_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21(location, relocVAddress, + targetVAddress, ref.addend()); + break; + case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: + relocR_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC(location, relocVAddress, + targetVAddress, ref.addend()); + break; + case R_AARCH64_TLSLE_ADD_TPREL_HI12: + relocR_AARCH64_TLSLE_ADD_TPREL_HI12(location, relocVAddress, targetVAddress, + ref.addend()); + break; + case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: + relocR_AARCH64_TLSLE_ADD_TPREL_LO12_NC(location, relocVAddress, + targetVAddress, ref.addend()); + break; + default: + return make_unhandled_reloc_error(); + } + + return std::error_code(); +} diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.h b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.h new file mode 100644 index 0000000000000..b1d3c09dc936c --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.h @@ -0,0 +1,33 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64RelocationHandler.h ------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef AARCH64_RELOCATION_HANDLER_H +#define AARCH64_RELOCATION_HANDLER_H + +#include "AArch64TargetHandler.h" + +namespace lld { +namespace elf { +typedef llvm::object::ELFType<llvm::support::little, 2, true> AArch64ELFType; + +template <class ELFT> class AArch64TargetLayout; + +class AArch64TargetRelocationHandler final : public TargetRelocationHandler { +public: + std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, + const lld::AtomLayout &, + const Reference &) const override; + + static const Registry::KindStrings kindStrings[]; +}; + +} // end namespace elf +} // end namespace lld + +#endif // AArch64_RELOCATION_HANDLER_H diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.cpp b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.cpp new file mode 100644 index 0000000000000..0bd12958b27bd --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.cpp @@ -0,0 +1,527 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.cpp -------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Defines the relocation processing pass for AArch64. This includes +/// GOT and PLT entries, TLS, COPY, and ifunc. +/// +/// This also includes additional behavior that gnu-ld and gold implement but +/// which is not specified anywhere. +/// +//===----------------------------------------------------------------------===// + +#include "AArch64RelocationPass.h" +#include "AArch64LinkingContext.h" +#include "Atoms.h" +#include "lld/Core/Simple.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Debug.h" + +using namespace lld; +using namespace lld::elf; +using namespace llvm::ELF; + +namespace { +// .got values +const uint8_t AArch64GotAtomContent[8] = {0}; + +// .plt value (entry 0) +const uint8_t AArch64Plt0AtomContent[32] = { + 0xf0, 0x7b, 0xbf, + 0xa9, // stp x16, x30, [sp,#-16]! + 0x10, 0x00, 0x00, + 0x90, // adrp x16, Page(eh_frame) + 0x11, 0x02, 0x40, + 0xf9, // ldr x17, [x16,#offset] + 0x10, 0x02, 0x00, + 0x91, // add x16, x16, #offset + 0x20, 0x02, 0x1f, + 0xd6, // br x17 + 0x1f, 0x20, 0x03, + 0xd5, // nop + 0x1f, 0x20, 0x03, + 0xd5, // nop + 0x1f, 0x20, 0x03, + 0xd5 // nop +}; + +// .plt values (other entries) +const uint8_t AArch64PltAtomContent[16] = { + 0x10, 0x00, 0x00, + 0x90, // adrp x16, PAGE(<GLOBAL_OFFSET_TABLE>) + 0x11, 0x02, 0x40, + 0xf9, // ldr x17, [x16,#offset] + 0x10, 0x02, 0x00, + 0x91, // add x16, x16, #offset + 0x20, 0x02, 0x1f, + 0xd6 // br x17 +}; + +/// \brief Atoms that are used by AArch64 dynamic linking +class AArch64GOTAtom : public GOTAtom { +public: + AArch64GOTAtom(const File &f, StringRef secName) : GOTAtom(f, secName) {} + + ArrayRef<uint8_t> rawContent() const override { + return ArrayRef<uint8_t>(AArch64GotAtomContent, 8); + } +}; + +class AArch64PLT0Atom : public PLT0Atom { +public: + AArch64PLT0Atom(const File &f) : PLT0Atom(f) {} + ArrayRef<uint8_t> rawContent() const override { + return ArrayRef<uint8_t>(AArch64Plt0AtomContent, 32); + } +}; + +class AArch64PLTAtom : public PLTAtom { +public: + AArch64PLTAtom(const File &f, StringRef secName) : PLTAtom(f, secName) {} + + ArrayRef<uint8_t> rawContent() const override { + return ArrayRef<uint8_t>(AArch64PltAtomContent, 16); + } +}; + +class ELFPassFile : public SimpleFile { +public: + ELFPassFile(const ELFLinkingContext &eti) : SimpleFile("ELFPassFile") { + setOrdinal(eti.getNextOrdinalAndIncrement()); + } + + llvm::BumpPtrAllocator _alloc; +}; + +/// \brief CRTP base for handling relocations. +template <class Derived> class AArch64RelocationPass : public Pass { + /// \brief Handle a specific reference. + void handleReference(const DefinedAtom &atom, const Reference &ref) { + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() + << "\t" << LLVM_FUNCTION_NAME << "()" + << ": Name of Defined Atom: " << atom.name().str(); + llvm::dbgs() << " kindValue: " << ref.kindValue() << "\n"); + if (ref.kindNamespace() != Reference::KindNamespace::ELF) + return; + assert(ref.kindArch() == Reference::KindArch::AArch64); + switch (ref.kindValue()) { + case R_AARCH64_ABS32: + case R_AARCH64_ABS16: + case R_AARCH64_ABS64: + case R_AARCH64_PREL16: + case R_AARCH64_PREL32: + case R_AARCH64_PREL64: + static_cast<Derived *>(this)->handlePlain(ref); + break; + case R_AARCH64_GOTREL32: + case R_AARCH64_GOTREL64: + static_cast<Derived *>(this)->handleGOT(ref); + break; + case R_AARCH64_ADR_PREL_PG_HI21: + static_cast<Derived *>(this)->handlePlain(ref); + break; + case R_AARCH64_LDST8_ABS_LO12_NC: + case R_AARCH64_LDST16_ABS_LO12_NC: + case R_AARCH64_LDST32_ABS_LO12_NC: + case R_AARCH64_LDST64_ABS_LO12_NC: + case R_AARCH64_LDST128_ABS_LO12_NC: + static_cast<Derived *>(this)->handlePlain(ref); + break; + case R_AARCH64_ADD_ABS_LO12_NC: + static_cast<Derived *>(this)->handlePlain(ref); + break; + case R_AARCH64_CALL26: + case R_AARCH64_JUMP26: + case R_AARCH64_CONDBR19: + static_cast<Derived *>(this)->handlePlain(ref); + break; + case R_AARCH64_TLSLE_ADD_TPREL_HI12: + case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: + static_cast<Derived *>(this)->handlePlain(ref); + break; + case R_AARCH64_ADR_GOT_PAGE: + case R_AARCH64_LD64_GOT_LO12_NC: + case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: + case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: + static_cast<Derived *>(this)->handleGOT(ref); + break; + } + } + +protected: + /// \brief get the PLT entry for a given IFUNC Atom. + /// + /// If the entry does not exist. Both the GOT and PLT entry is created. + const PLTAtom *getIFUNCPLTEntry(const DefinedAtom *da) { + auto plt = _pltMap.find(da); + if (plt != _pltMap.end()) + return plt->second; + auto ga = new (_file._alloc) AArch64GOTAtom(_file, ".got.plt"); + ga->addReferenceELF_AArch64(R_AARCH64_IRELATIVE, 0, da, 0); + auto pa = new (_file._alloc) AArch64PLTAtom(_file, ".plt"); + pa->addReferenceELF_AArch64(R_AARCH64_PREL32, 2, ga, -4); +#ifndef NDEBUG + ga->_name = "__got_ifunc_"; + ga->_name += da->name(); + pa->_name = "__plt_ifunc_"; + pa->_name += da->name(); +#endif + _gotMap[da] = ga; + _pltMap[da] = pa; + _gotVector.push_back(ga); + _pltVector.push_back(pa); + return pa; + } + + /// \brief Redirect the call to the PLT stub for the target IFUNC. + /// + /// This create a PLT and GOT entry for the IFUNC if one does not exist. The + /// GOT entry and a IRELATIVE relocation to the original target resolver. + std::error_code handleIFUNC(const Reference &ref) { + auto target = dyn_cast_or_null<const DefinedAtom>(ref.target()); + if (target && target->contentType() == DefinedAtom::typeResolver) + const_cast<Reference &>(ref).setTarget(getIFUNCPLTEntry(target)); + return std::error_code(); + } + + /// \brief Create a GOT entry for the TP offset of a TLS atom. + const GOTAtom *getGOTTPOFF(const Atom *atom) { + auto got = _gotMap.find(atom); + if (got == _gotMap.end()) { + auto g = new (_file._alloc) AArch64GOTAtom(_file, ".got"); + g->addReferenceELF_AArch64(R_AARCH64_GOTREL64, 0, atom, 0); +#ifndef NDEBUG + g->_name = "__got_tls_"; + g->_name += atom->name(); +#endif + _gotMap[atom] = g; + _gotVector.push_back(g); + return g; + } + return got->second; + } + + /// \brief Create a TPOFF64 GOT entry and change the relocation to a PC32 to + /// the GOT. + void handleGOTTPOFF(const Reference &ref) { + const_cast<Reference &>(ref).setTarget(getGOTTPOFF(ref.target())); + const_cast<Reference &>(ref).setKindValue(R_AARCH64_PREL32); + } + + /// \brief Create a GOT entry containing 0. + const GOTAtom *getNullGOT() { + if (!_null) { + _null = new (_file._alloc) AArch64GOTAtom(_file, ".got.plt"); +#ifndef NDEBUG + _null->_name = "__got_null"; +#endif + } + return _null; + } + + const GOTAtom *getGOT(const DefinedAtom *da) { + auto got = _gotMap.find(da); + if (got == _gotMap.end()) { + auto g = new (_file._alloc) AArch64GOTAtom(_file, ".got"); + g->addReferenceELF_AArch64(R_AARCH64_ABS64, 0, da, 0); +#ifndef NDEBUG + g->_name = "__got_"; + g->_name += da->name(); +#endif + _gotMap[da] = g; + _gotVector.push_back(g); + return g; + } + return got->second; + } + +public: + AArch64RelocationPass(const ELFLinkingContext &ctx) + : _file(ctx), _ctx(ctx), _null(nullptr), _PLT0(nullptr), _got0(nullptr), + _got1(nullptr) {} + + /// \brief Do the pass. + /// + /// The goal here is to first process each reference individually. Each call + /// to handleReference may modify the reference itself and/or create new + /// atoms which must be stored in one of the maps below. + /// + /// After all references are handled, the atoms created during that are all + /// added to mf. + void perform(std::unique_ptr<MutableFile> &mf) override { + ScopedTask task(getDefaultDomain(), "AArch64 GOT/PLT Pass"); + DEBUG_WITH_TYPE( + "AArch64", llvm::dbgs() << "Undefined Atoms" + << "\n"; + for (const auto &atom + : mf->undefined()) { + llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; + } llvm::dbgs() + << "Shared Library Atoms" + << "\n"; + for (const auto &atom + : mf->sharedLibrary()) { + llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; + } llvm::dbgs() + << "Absolute Atoms" + << "\n"; + for (const auto &atom + : mf->absolute()) { + llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; + } + // Process all references. + llvm::dbgs() + << "Defined Atoms" + << "\n"); + for (const auto &atom : mf->defined()) { + for (const auto &ref : *atom) { + handleReference(*atom, *ref); + } + } + + // Add all created atoms to the link. + uint64_t ordinal = 0; + if (_PLT0) { + _PLT0->setOrdinal(ordinal++); + mf->addAtom(*_PLT0); + } + for (auto &plt : _pltVector) { + plt->setOrdinal(ordinal++); + mf->addAtom(*plt); + } + if (_null) { + _null->setOrdinal(ordinal++); + mf->addAtom(*_null); + } + if (_PLT0) { + _got0->setOrdinal(ordinal++); + _got1->setOrdinal(ordinal++); + mf->addAtom(*_got0); + mf->addAtom(*_got1); + } + for (auto &got : _gotVector) { + got->setOrdinal(ordinal++); + mf->addAtom(*got); + } + for (auto obj : _objectVector) { + obj->setOrdinal(ordinal++); + mf->addAtom(*obj); + } + } + +protected: + /// \brief Owner of all the Atoms created by this pass. + ELFPassFile _file; + const ELFLinkingContext &_ctx; + + /// \brief Map Atoms to their GOT entries. + llvm::DenseMap<const Atom *, GOTAtom *> _gotMap; + + /// \brief Map Atoms to their PLT entries. + llvm::DenseMap<const Atom *, PLTAtom *> _pltMap; + + /// \brief Map Atoms to their Object entries. + llvm::DenseMap<const Atom *, ObjectAtom *> _objectMap; + + /// \brief the list of GOT/PLT atoms + std::vector<GOTAtom *> _gotVector; + std::vector<PLTAtom *> _pltVector; + std::vector<ObjectAtom *> _objectVector; + + /// \brief GOT entry that is always 0. Used for undefined weaks. + GOTAtom *_null; + + /// \brief The got and plt entries for .PLT0. This is used to call into the + /// dynamic linker for symbol resolution. + /// @{ + PLT0Atom *_PLT0; + GOTAtom *_got0; + GOTAtom *_got1; + /// @} +}; + +/// This implements the static relocation model. Meaning GOT and PLT entries are +/// not created for references that can be directly resolved. These are +/// converted to a direct relocation. For entries that do require a GOT or PLT +/// entry, that entry is statically bound. +/// +/// TLS always assumes module 1 and attempts to remove indirection. +class AArch64StaticRelocationPass final + : public AArch64RelocationPass<AArch64StaticRelocationPass> { +public: + AArch64StaticRelocationPass(const elf::AArch64LinkingContext &ctx) + : AArch64RelocationPass(ctx) {} + + std::error_code handlePlain(const Reference &ref) { return handleIFUNC(ref); } + + std::error_code handlePLT32(const Reference &ref) { + // __tls_get_addr is handled elsewhere. + if (ref.target() && ref.target()->name() == "__tls_get_addr") { + const_cast<Reference &>(ref).setKindValue(R_AARCH64_NONE); + return std::error_code(); + } + // Static code doesn't need PLTs. + const_cast<Reference &>(ref).setKindValue(R_AARCH64_PREL32); + // Handle IFUNC. + if (const DefinedAtom *da = + dyn_cast_or_null<const DefinedAtom>(ref.target())) + if (da->contentType() == DefinedAtom::typeResolver) + return handleIFUNC(ref); + return std::error_code(); + } + + std::error_code handleGOT(const Reference &ref) { + if (isa<UndefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getNullGOT()); + else if (const DefinedAtom *da = dyn_cast<const DefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getGOT(da)); + return std::error_code(); + } +}; + +class AArch64DynamicRelocationPass final + : public AArch64RelocationPass<AArch64DynamicRelocationPass> { +public: + AArch64DynamicRelocationPass(const elf::AArch64LinkingContext &ctx) + : AArch64RelocationPass(ctx) {} + + const PLT0Atom *getPLT0() { + if (_PLT0) + return _PLT0; + // Fill in the null entry. + getNullGOT(); + _PLT0 = new (_file._alloc) AArch64PLT0Atom(_file); + _got0 = new (_file._alloc) AArch64GOTAtom(_file, ".got.plt"); + _got1 = new (_file._alloc) AArch64GOTAtom(_file, ".got.plt"); + _PLT0->addReferenceELF_AArch64(R_AARCH64_ADR_GOT_PAGE, 4, _got0, 0); + _PLT0->addReferenceELF_AArch64(R_AARCH64_LD64_GOT_LO12_NC, 8, _got1, 0); + _PLT0->addReferenceELF_AArch64(ADD_AARCH64_GOTRELINDEX, 12, _got1, 0); +#ifndef NDEBUG + _PLT0->_name = "__PLT0"; + _got0->_name = "__got0"; + _got1->_name = "__got1"; +#endif + return _PLT0; + } + + const PLTAtom *getPLTEntry(const Atom *a) { + auto plt = _pltMap.find(a); + if (plt != _pltMap.end()) + return plt->second; + auto ga = new (_file._alloc) AArch64GOTAtom(_file, ".got.plt"); + ga->addReferenceELF_AArch64(R_AARCH64_JUMP_SLOT, 0, a, 0); + auto pa = new (_file._alloc) AArch64PLTAtom(_file, ".plt"); + pa->addReferenceELF_AArch64(R_AARCH64_ADR_GOT_PAGE, 0, ga, 0); + pa->addReferenceELF_AArch64(R_AARCH64_LD64_GOT_LO12_NC, 4, ga, 0); + pa->addReferenceELF_AArch64(ADD_AARCH64_GOTRELINDEX, 8, ga, 0); + pa->addReferenceELF_AArch64(R_AARCH64_NONE, 12, getPLT0(), 0); + // Set the starting address of the got entry to the first instruction in + // the plt0 entry. + ga->addReferenceELF_AArch64(R_AARCH64_ABS32, 0, getPLT0(), 0); +#ifndef NDEBUG + ga->_name = "__got_"; + ga->_name += a->name(); + pa->_name = "__plt_"; + pa->_name += a->name(); +#endif + _gotMap[a] = ga; + _pltMap[a] = pa; + _gotVector.push_back(ga); + _pltVector.push_back(pa); + return pa; + } + + const ObjectAtom *getObjectEntry(const SharedLibraryAtom *a) { + auto obj = _objectMap.find(a); + if (obj != _objectMap.end()) + return obj->second; + + auto oa = new (_file._alloc) ObjectAtom(_file); + // This needs to point to the atom that we just created. + oa->addReferenceELF_AArch64(R_AARCH64_COPY, 0, oa, 0); + + oa->_name = a->name(); + oa->_size = a->size(); + + _objectMap[a] = oa; + _objectVector.push_back(oa); + return oa; + } + + std::error_code handlePlain(const Reference &ref) { + if (!ref.target()) + return std::error_code(); + if (auto sla = dyn_cast<SharedLibraryAtom>(ref.target())) { + if (sla->type() == SharedLibraryAtom::Type::Data) + const_cast<Reference &>(ref).setTarget(getObjectEntry(sla)); + else if (sla->type() == SharedLibraryAtom::Type::Code) + const_cast<Reference &>(ref).setTarget(getPLTEntry(sla)); + } else + return handleIFUNC(ref); + return std::error_code(); + } + + std::error_code handlePLT32(const Reference &ref) { + // Turn this into a PC32 to the PLT entry. + const_cast<Reference &>(ref).setKindValue(R_AARCH64_PREL32); + // Handle IFUNC. + if (const DefinedAtom *da = + dyn_cast_or_null<const DefinedAtom>(ref.target())) + if (da->contentType() == DefinedAtom::typeResolver) + return handleIFUNC(ref); + if (isa<const SharedLibraryAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getPLTEntry(ref.target())); + return std::error_code(); + } + + const GOTAtom *getSharedGOT(const SharedLibraryAtom *sla) { + auto got = _gotMap.find(sla); + if (got == _gotMap.end()) { + auto g = new (_file._alloc) AArch64GOTAtom(_file, ".got"); + g->addReferenceELF_AArch64(R_AARCH64_GLOB_DAT, 0, sla, 0); +#ifndef NDEBUG + g->_name = "__got_"; + g->_name += sla->name(); +#endif + _gotMap[sla] = g; + _gotVector.push_back(g); + return g; + } + return got->second; + } + + std::error_code handleGOT(const Reference &ref) { + if (isa<UndefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getNullGOT()); + else if (const DefinedAtom *da = dyn_cast<const DefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getGOT(da)); + else if (const auto sla = dyn_cast<const SharedLibraryAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getSharedGOT(sla)); + return std::error_code(); + } +}; +} // end anon namespace + +std::unique_ptr<Pass> +lld::elf::createAArch64RelocationPass(const AArch64LinkingContext &ctx) { + switch (ctx.getOutputELFType()) { + case llvm::ELF::ET_EXEC: + if (ctx.isDynamic()) + return llvm::make_unique<AArch64DynamicRelocationPass>(ctx); + return llvm::make_unique<AArch64StaticRelocationPass>(ctx); + case llvm::ELF::ET_DYN: + return llvm::make_unique<AArch64DynamicRelocationPass>(ctx); + case llvm::ELF::ET_REL: + return nullptr; + default: + llvm_unreachable("Unhandled output file type"); + } +} diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.h b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.h new file mode 100644 index 0000000000000..73d784e3b52d8 --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.h @@ -0,0 +1,32 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64RelocationPass.h ---------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Declares the relocation processing pass for AArch64. This includes +/// GOT and PLT entries, TLS, COPY, and ifunc. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_ELF_AARCH64_AARCH64_RELOCATION_PASS_H +#define LLD_READER_WRITER_ELF_AARCH64_AARCH64_RELOCATION_PASS_H + +#include <memory> + +namespace lld { +class Pass; +namespace elf { +class AArch64LinkingContext; + +/// \brief Create AArch64 relocation pass for the given linking context. +std::unique_ptr<Pass> +createAArch64RelocationPass(const AArch64LinkingContext &); +} +} + +#endif diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.cpp b/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.cpp new file mode 100644 index 0000000000000..607f767f8b8a4 --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.cpp @@ -0,0 +1,52 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.cpp --------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Atoms.h" +#include "AArch64DynamicLibraryWriter.h" +#include "AArch64ExecutableWriter.h" +#include "AArch64LinkingContext.h" +#include "AArch64TargetHandler.h" + +using namespace lld; +using namespace elf; + +AArch64TargetHandler::AArch64TargetHandler(AArch64LinkingContext &context) + : _context(context), + _AArch64TargetLayout(new AArch64TargetLayout<AArch64ELFType>(context)), + _AArch64RelocationHandler(new AArch64TargetRelocationHandler()) {} + +void AArch64TargetHandler::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, + Reference::KindArch::AArch64, kindStrings); +} + +std::unique_ptr<Writer> AArch64TargetHandler::getWriter() { + switch (this->_context.getOutputELFType()) { + case llvm::ELF::ET_EXEC: + return std::unique_ptr<Writer>(new AArch64ExecutableWriter<AArch64ELFType>( + _context, *_AArch64TargetLayout.get())); + case llvm::ELF::ET_DYN: + return std::unique_ptr<Writer>( + new AArch64DynamicLibraryWriter<AArch64ELFType>( + _context, *_AArch64TargetLayout.get())); + case llvm::ELF::ET_REL: + llvm_unreachable("TODO: support -r mode"); + default: + llvm_unreachable("unsupported output type"); + } +} + +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), + +const Registry::KindStrings AArch64TargetHandler::kindStrings[] = { +#include "llvm/Support/ELFRelocs/AArch64.def" + LLD_KIND_STRING_END +}; + +#undef ELF_RELOC diff --git a/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.h b/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.h new file mode 100644 index 0000000000000..4eb6786cdf1fb --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.h @@ -0,0 +1,64 @@ +//===- lib/ReaderWriter/ELF/AArch64/AArch64TargetHandler.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_AARCH64_AARCH64_TARGET_HANDLER_H +#define LLD_READER_WRITER_ELF_AARCH64_AARCH64_TARGET_HANDLER_H + +#include "AArch64ELFFile.h" +#include "AArch64ELFReader.h" +#include "AArch64RelocationHandler.h" +#include "DefaultTargetHandler.h" +#include "TargetLayout.h" +#include "lld/Core/Simple.h" + +namespace lld { +namespace elf { +class AArch64LinkingContext; + +template <class ELFT> class AArch64TargetLayout : public TargetLayout<ELFT> { +public: + AArch64TargetLayout(AArch64LinkingContext &context) + : TargetLayout<ELFT>(context) {} +}; + +class AArch64TargetHandler final : public DefaultTargetHandler<AArch64ELFType> { +public: + AArch64TargetHandler(AArch64LinkingContext &context); + + AArch64TargetLayout<AArch64ELFType> &getTargetLayout() override { + return *(_AArch64TargetLayout.get()); + } + + void registerRelocationNames(Registry ®istry) override; + + const AArch64TargetRelocationHandler &getRelocationHandler() const override { + return *(_AArch64RelocationHandler.get()); + } + + std::unique_ptr<Reader> getObjReader() override { + return std::unique_ptr<Reader>(new AArch64ELFObjectReader(_context)); + } + + std::unique_ptr<Reader> getDSOReader() override { + return std::unique_ptr<Reader>(new AArch64ELFDSOReader(_context)); + } + + std::unique_ptr<Writer> getWriter() override; + +private: + static const Registry::KindStrings kindStrings[]; + AArch64LinkingContext &_context; + std::unique_ptr<AArch64TargetLayout<AArch64ELFType>> _AArch64TargetLayout; + std::unique_ptr<AArch64TargetRelocationHandler> _AArch64RelocationHandler; +}; + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/AArch64/CMakeLists.txt b/lib/ReaderWriter/ELF/AArch64/CMakeLists.txt new file mode 100644 index 0000000000000..de94a4df5078a --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/CMakeLists.txt @@ -0,0 +1,12 @@ +add_llvm_library(lldAArch64ELFTarget + AArch64LinkingContext.cpp + AArch64TargetHandler.cpp + AArch64RelocationHandler.cpp + AArch64RelocationPass.cpp + LINK_LIBS + lldELF + lldReaderWriter + lldCore + LLVMObject + LLVMSupport + ) diff --git a/lib/ReaderWriter/ELF/AArch64/Makefile b/lib/ReaderWriter/ELF/AArch64/Makefile new file mode 100644 index 0000000000000..02cff4747d0d2 --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/Makefile @@ -0,0 +1,15 @@ +##===- lld/lib/ReaderWriter/ELF/AArch64/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 := lldAArch64ELFTarget +USEDLIBS = lldCore.a +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF/AArch64 -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/AArch64/TODO.rst b/lib/ReaderWriter/ELF/AArch64/TODO.rst new file mode 100644 index 0000000000000..aa6f616ff33fb --- /dev/null +++ b/lib/ReaderWriter/ELF/AArch64/TODO.rst @@ -0,0 +1,15 @@ +ELF AArch64 +~~~~~~~~~~~ + +Unimplemented Features +###################### + +* Just about everything! + +Unimplemented Relocations +######################### + +All of these relocations are defined in: +http://infocenter.arm.com/help/topic/com.arm.doc.ihi0056b/IHI0056B_aaelf64.pdf + + diff --git a/lib/ReaderWriter/ELF/ARM/ARMELFFile.h b/lib/ReaderWriter/ELF/ARM/ARMELFFile.h new file mode 100644 index 0000000000000..bc5ee35b8213b --- /dev/null +++ b/lib/ReaderWriter/ELF/ARM/ARMELFFile.h @@ -0,0 +1,97 @@ +//===--------- lib/ReaderWriter/ELF/ARM/ARMELFFile.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_ARM_ARM_ELF_FILE_H +#define LLD_READER_WRITER_ELF_ARM_ARM_ELF_FILE_H + +#include "ELFReader.h" + +namespace lld { +namespace elf { + +class ARMLinkingContext; + +template <class ELFT> class ARMELFDefinedAtom : public ELFDefinedAtom<ELFT> { + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + +public: + ARMELFDefinedAtom(const ELFFile<ELFT> &file, StringRef symbolName, + StringRef sectionName, const Elf_Sym *symbol, + const Elf_Shdr *section, ArrayRef<uint8_t> contentData, + unsigned int referenceStart, unsigned int referenceEnd, + std::vector<ELFReference<ELFT> *> &referenceList) + : ELFDefinedAtom<ELFT>(file, symbolName, sectionName, symbol, section, + contentData, referenceStart, referenceEnd, + referenceList) {} + + bool isThumbFunc(const Elf_Sym *symbol) const { + return symbol->getType() == llvm::ELF::STT_FUNC && + (static_cast<uint64_t>(symbol->st_value) & 0x1); + } + + /// Correct st_value for symbols addressing Thumb instructions + /// by removing its zero bit. + uint64_t getSymbolValue(const Elf_Sym *symbol) const override { + const auto value = static_cast<uint64_t>(symbol->st_value); + return isThumbFunc(symbol) ? value & ~0x1 : value; + } + + DefinedAtom::CodeModel codeModel() const override { + if (isThumbFunc(this->_symbol)) + return DefinedAtom::codeARMThumb; + return DefinedAtom::codeNA; + } +}; + +template <class ELFT> class ARMELFFile : public ELFFile<ELFT> { +public: + ARMELFFile(std::unique_ptr<MemoryBuffer> mb, ARMLinkingContext &ctx) + : ELFFile<ELFT>(std::move(mb), ctx) {} + + static ErrorOr<std::unique_ptr<ARMELFFile>> + create(std::unique_ptr<MemoryBuffer> mb, ARMLinkingContext &ctx) { + return std::unique_ptr<ARMELFFile<ELFT>>( + new ARMELFFile<ELFT>(std::move(mb), ctx)); + } + +private: + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + + /// Correct st_value for symbols addressing Thumb instructions + /// by removing its zero bit. + uint64_t getSymbolValue(const Elf_Sym *symbol) const override { + const auto value = static_cast<uint64_t>(symbol->st_value); + return symbol->getType() == llvm::ELF::STT_FUNC ? value & ~0x1 : value; + } + + /// Process the Defined symbol and create an atom for it. + ErrorOr<ELFDefinedAtom<ELFT> *> handleDefinedSymbol(StringRef symName, + StringRef sectionName, + const Elf_Sym *sym, const Elf_Shdr *sectionHdr, + ArrayRef<uint8_t> contentData, + unsigned int referenceStart, unsigned int referenceEnd, + std::vector<ELFReference<ELFT> *> &referenceList) override { + return new (this->_readerStorage) ARMELFDefinedAtom<ELFT>( + *this, symName, sectionName, sym, sectionHdr, contentData, + referenceStart, referenceEnd, referenceList); + } +}; + +template <class ELFT> class ARMDynamicFile : public DynamicFile<ELFT> { +public: + ARMDynamicFile(const ARMLinkingContext &context, StringRef name) + : DynamicFile<ELFT>(context, name) {} +}; + +} // elf +} // lld + +#endif // LLD_READER_WRITER_ELF_ARM_ARM_ELF_FILE_H diff --git a/lib/ReaderWriter/ELF/ARM/ARMELFReader.h b/lib/ReaderWriter/ELF/ARM/ARMELFReader.h new file mode 100644 index 0000000000000..31af531563ea5 --- /dev/null +++ b/lib/ReaderWriter/ELF/ARM/ARMELFReader.h @@ -0,0 +1,62 @@ +//===--------- lib/ReaderWriter/ELF/ARM/ARMELFReader.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_ARM_ARM_ELF_READER_H +#define LLD_READER_WRITER_ARM_ARM_ELF_READER_H + +#include "ARMELFFile.h" +#include "ELFReader.h" + +namespace lld { +namespace elf { + +typedef llvm::object::ELFType<llvm::support::little, 2, false> ARMELFType; + +struct ARMDynamicFileCreateELFTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::SharedLibraryFile>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + ARMLinkingContext &ctx) { + return lld::elf::ARMDynamicFile<ELFT>::create(std::move(mb), ctx); + } +}; + +struct ARMELFFileCreateELFTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::File>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + ARMLinkingContext &ctx) { + return lld::elf::ARMELFFile<ELFT>::create(std::move(mb), ctx); + } +}; + +class ARMELFObjectReader + : public ELFObjectReader<ARMELFType, ARMELFFileCreateELFTraits, + ARMLinkingContext> { +public: + ARMELFObjectReader(ARMLinkingContext &ctx) + : ELFObjectReader<ARMELFType, ARMELFFileCreateELFTraits, + ARMLinkingContext>(ctx, llvm::ELF::EM_ARM) {} +}; + +class ARMELFDSOReader + : public ELFDSOReader<ARMELFType, ARMDynamicFileCreateELFTraits, + ARMLinkingContext> { +public: + ARMELFDSOReader(ARMLinkingContext &ctx) + : ELFDSOReader<ARMELFType, ARMDynamicFileCreateELFTraits, + ARMLinkingContext>(ctx, llvm::ELF::EM_ARM) {} +}; + +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_ARM_ARM_ELF_READER_H diff --git a/lib/ReaderWriter/ELF/ARM/ARMExecutableWriter.h b/lib/ReaderWriter/ELF/ARM/ARMExecutableWriter.h new file mode 100644 index 0000000000000..19311d516e4de --- /dev/null +++ b/lib/ReaderWriter/ELF/ARM/ARMExecutableWriter.h @@ -0,0 +1,121 @@ +//===--------- lib/ReaderWriter/ELF/ARM/ARMExecutableWriter.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_ARM_ARM_EXECUTABLE_WRITER_H +#define LLD_READER_WRITER_ELF_ARM_ARM_EXECUTABLE_WRITER_H + +#include "ExecutableWriter.h" +#include "ARMLinkingContext.h" +#include "ARMTargetHandler.h" +#include "ARMSymbolTable.h" + +namespace { +const char *gotSymbol = "_GLOBAL_OFFSET_TABLE_"; +} + +namespace lld { +namespace elf { + +template <class ELFT> +class ARMExecutableWriter : public ExecutableWriter<ELFT> { +public: + ARMExecutableWriter(ARMLinkingContext &context, + ARMTargetLayout<ELFT> &layout); + +protected: + // Add any runtime files and their atoms to the output + bool createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; + + void finalizeDefaultAtomValues() override; + + void addDefaultAtoms() override { + ExecutableWriter<ELFT>::addDefaultAtoms(); + } + + /// \brief Create symbol table. + unique_bump_ptr<SymbolTable<ELFT>> createSymbolTable() override; + + void processUndefinedSymbol(StringRef symName, + RuntimeFile<ELFT> &file) const override; + + // Setup the ELF header. + std::error_code setELFHeader() override; + +private: + ARMLinkingContext &_context; + ARMTargetLayout<ELFT> &_armLayout; +}; + +template <class ELFT> +ARMExecutableWriter<ELFT>::ARMExecutableWriter(ARMLinkingContext &context, + ARMTargetLayout<ELFT> &layout) + : ExecutableWriter<ELFT>(context, layout), _context(context), + _armLayout(layout) {} + +template <class ELFT> +bool ARMExecutableWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + ExecutableWriter<ELFT>::createImplicitFiles(result); + return true; +} + +template <class ELFT> +void ARMExecutableWriter<ELFT>::finalizeDefaultAtomValues() { + // Finalize the atom values that are part of the parent. + ExecutableWriter<ELFT>::finalizeDefaultAtomValues(); + auto gotAtomIter = _armLayout.findAbsoluteAtom(gotSymbol); + if (gotAtomIter != _armLayout.absoluteAtoms().end()) { + auto *gotAtom = *gotAtomIter; + if (auto gotpltSection = _armLayout.findOutputSection(".got.plt")) + gotAtom->_virtualAddr = gotpltSection->virtualAddr(); + else if (auto gotSection = _armLayout.findOutputSection(".got")) + gotAtom->_virtualAddr = gotSection->virtualAddr(); + else + gotAtom->_virtualAddr = 0; + } + // TODO: resolve addresses of __exidx_start/_end atoms +} + +template <class ELFT> +unique_bump_ptr<SymbolTable<ELFT>> + ARMExecutableWriter<ELFT>::createSymbolTable() { + return unique_bump_ptr<SymbolTable<ELFT>>( + new (this->_alloc) ARMSymbolTable<ELFT>(this->_context)); +} + +template <class ELFT> +void ARMExecutableWriter<ELFT>::processUndefinedSymbol( + StringRef symName, RuntimeFile<ELFT> &file) const { + if (symName == gotSymbol) { + file.addAbsoluteAtom(gotSymbol); + } else if (symName.startswith("__exidx")) { + file.addAbsoluteAtom("__exidx_start"); + file.addAbsoluteAtom("__exidx_end"); + } +} + +template <class ELFT> +std::error_code ARMExecutableWriter<ELFT>::setELFHeader() { + if (std::error_code ec = ExecutableWriter<ELFT>::setELFHeader()) + return ec; + + // Fixup entry point for Thumb code. + StringRef entryName = _context.entrySymbolName(); + if (const AtomLayout *al = _armLayout.findAtomLayoutByName(entryName)) { + const auto *ea = dyn_cast<DefinedAtom>(al->_atom); + if (ea && ea->codeModel() == DefinedAtom::codeARMThumb) + this->_elfHeader->e_entry(al->_virtualAddr | 0x1); + } + + return std::error_code(); +} + +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_ELF_ARM_ARM_EXECUTABLE_WRITER_H diff --git a/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.cpp b/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.cpp new file mode 100644 index 0000000000000..5f24366742684 --- /dev/null +++ b/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.cpp @@ -0,0 +1,34 @@ +//===--------- lib/ReaderWriter/ELF/ARM/ARMLinkingContext.cpp -------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ARMLinkingContext.h" +#include "ARMRelocationPass.h" +#include "ARMTargetHandler.h" + +using namespace lld; +using namespace lld::elf; + +std::unique_ptr<ELFLinkingContext> +elf::ARMLinkingContext::create(llvm::Triple triple) { + if (triple.getArch() == llvm::Triple::arm) + return std::unique_ptr<ELFLinkingContext>( + new elf::ARMLinkingContext(triple)); + return nullptr; +} + +elf::ARMLinkingContext::ARMLinkingContext(llvm::Triple triple) + : ELFLinkingContext(triple, std::unique_ptr<TargetHandlerBase>( + new ARMTargetHandler(*this))) {} + +void elf::ARMLinkingContext::addPasses(PassManager &pm) { + auto pass = createARMRelocationPass(*this); + if (pass) + pm.add(std::move(pass)); + ELFLinkingContext::addPasses(pm); +} diff --git a/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h b/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h new file mode 100644 index 0000000000000..249b79c4f07d0 --- /dev/null +++ b/lib/ReaderWriter/ELF/ARM/ARMLinkingContext.h @@ -0,0 +1,36 @@ +//===--------- lib/ReaderWriter/ELF/ARM/ARMLinkingContext.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_ARM_ARM_LINKING_CONTEXT_H +#define LLD_READER_WRITER_ELF_ARM_ARM_LINKING_CONTEXT_H + +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/ELF.h" + +namespace lld { +namespace elf { + +class ARMLinkingContext final : public ELFLinkingContext { +public: + static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + ARMLinkingContext(llvm::Triple); + + void addPasses(PassManager &) override; + + uint64_t getBaseAddress() const override { + if (_baseAddress == 0) + return 0x400000; + return _baseAddress; + } +}; +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp b/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp new file mode 100644 index 0000000000000..d24fdf0fa410a --- /dev/null +++ b/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp @@ -0,0 +1,500 @@ +//===--------- lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.cpp ----------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ARMTargetHandler.h" +#include "ARMLinkingContext.h" + +#include "llvm/Support/Debug.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/MathExtras.h" + +using namespace lld; +using namespace lld::elf; +using namespace llvm::support::endian; + +static Reference::Addend readAddend_THM_MOV(const uint8_t *location) { + const uint16_t halfHi = read16le(location); + const uint16_t halfLo = read16le(location + 2); + + const uint16_t imm8 = halfLo & 0xFF; + const uint16_t imm3 = (halfLo >> 12) & 0x7; + + const uint16_t imm4 = halfHi & 0xF; + const uint16_t bitI = (halfHi >> 10) & 0x1; + + const auto result = int16_t((imm4 << 12) | (bitI << 11) | (imm3 << 8) | imm8); + return result; +} + +static Reference::Addend readAddend_ARM_MOV(const uint8_t *location) { + const uint32_t value = read32le(location); + + const uint32_t imm12 = value & 0xFFF; + const uint32_t imm4 = (value >> 16) & 0xF; + + const auto result = int32_t((imm4 << 12) | imm12); + return result; +} + +static Reference::Addend readAddend_THM_CALL(const uint8_t *location) { + const uint16_t halfHi = read16le(location); + const uint16_t halfLo = read16le(location + 2); + + const uint16_t imm10 = halfHi & 0x3FF; + const uint16_t bitS = (halfHi >> 10) & 0x1; + + const uint16_t imm11 = halfLo & 0x7FF; + const uint16_t bitJ2 = (halfLo >> 11) & 0x1; + const uint16_t bitI2 = (~(bitJ2 ^ bitS)) & 0x1; + const uint16_t bitJ1 = (halfLo >> 13) & 0x1; + const uint16_t bitI1 = (~(bitJ1 ^ bitS)) & 0x1; + + const auto result = int32_t((bitS << 24) | (bitI1 << 23) | (bitI2 << 22) | + (imm10 << 12) | (imm11 << 1)); + return llvm::SignExtend64<25>(result); +} + +static Reference::Addend readAddend_ARM_CALL(const uint8_t *location) { + const uint32_t value = read32le(location); + + const bool isBLX = (value & 0xF0000000) == 0xF0000000; + const uint32_t bitH = isBLX ? ((value & 0x1000000) >> 24) : 0; + + const auto result = int32_t(((value & 0xFFFFFF) << 2) | (bitH << 1)); + return llvm::SignExtend64<26>(result); +} + +static Reference::Addend readAddend_THM_JUMP11(const uint8_t *location) { + const auto value = read16le(location); + const uint16_t imm11 = value & 0x7FF; + + return llvm::SignExtend32<12>(imm11 << 1); +} + +static Reference::Addend readAddend(const uint8_t *location, + Reference::KindValue kindValue) { + switch (kindValue) { + case R_ARM_ABS32: + case R_ARM_REL32: + case R_ARM_TLS_IE32: + case R_ARM_TLS_LE32: + return (int32_t)read32le(location); + case R_ARM_PREL31: + return (int32_t)(read32le(location) & 0x7FFFFFFF); + case R_ARM_THM_CALL: + case R_ARM_THM_JUMP24: + return readAddend_THM_CALL(location); + case R_ARM_THM_JUMP11: + return readAddend_THM_JUMP11(location); + case R_ARM_CALL: + case R_ARM_JUMP24: + return readAddend_ARM_CALL(location); + case R_ARM_MOVW_ABS_NC: + case R_ARM_MOVT_ABS: + return readAddend_ARM_MOV(location); + case R_ARM_THM_MOVW_ABS_NC: + case R_ARM_THM_MOVT_ABS: + return readAddend_THM_MOV(location); + default: + return 0; + } +} + +static inline void applyArmReloc(uint8_t *location, uint32_t result, + uint32_t mask = 0xFFFFFFFF) { + assert(!(result & ~mask)); + write32le(location, (read32le(location) & ~mask) | (result & mask)); +} + +static inline void applyThmReloc(uint8_t *location, uint16_t resHi, + uint16_t resLo, uint16_t maskHi, + uint16_t maskLo = 0xFFFF) { + assert(!(resHi & ~maskHi) && !(resLo & ~maskLo)); + write16le(location, (read16le(location) & ~maskHi) | (resHi & maskHi)); + location += 2; + write16le(location, (read16le(location) & ~maskLo) | (resLo & maskLo)); +} + +static inline void applyThumb16Reloc(uint8_t *location, uint16_t result, + uint16_t mask = 0xFFFF) { + assert(!(result & ~mask)); + write16le(location, (read16le(location) & ~mask) | (result & mask)); +} + +/// \brief R_ARM_ABS32 - (S + A) | T +static void relocR_ARM_ABS32(uint8_t *location, uint64_t P, uint64_t S, + int64_t A, bool addressesThumb) { + uint64_t T = addressesThumb; + uint32_t result = (uint32_t)((S + A) | T); + + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + applyArmReloc(location, result); +} + +/// \brief R_ARM_REL32 - ((S + A) | T) - P +static void relocR_ARM_REL32(uint8_t *location, uint64_t P, uint64_t S, + int64_t A, bool addressesThumb) { + uint64_t T = addressesThumb; + uint32_t result = (uint32_t)(((S + A) | T) - P); + + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + applyArmReloc(location, result); +} + +/// \brief R_ARM_PREL31 - ((S + A) | T) - P +static void relocR_ARM_PREL31(uint8_t *location, uint64_t P, uint64_t S, + int64_t A, bool addressesThumb) { + uint64_t T = addressesThumb; + uint32_t result = (uint32_t)(((S + A) | T) - P); + const uint32_t mask = 0x7FFFFFFF; + uint32_t rel31 = result & mask; + + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result); + llvm::dbgs() << " rel31: 0x" << Twine::utohexstr(rel31) << "\n"); + + applyArmReloc(location, rel31, mask); +} + +/// \brief Relocate B/BL instructions. useJs defines whether J1 & J2 are used +static void relocR_ARM_THM_B_L(uint8_t *location, uint32_t result, bool useJs) { + result = (result & 0x01FFFFFE) >> 1; + + const uint16_t imm10 = (result >> 11) & 0x3FF; + const uint16_t bitS = (result >> 23) & 0x1; + const uint16_t resHi = (bitS << 10) | imm10; + + const uint16_t imm11 = result & 0x7FF; + const uint16_t bitJ2 = useJs ? ((result >> 21) & 0x1) : bitS; + const uint16_t bitI2 = (~(bitJ2 ^ bitS)) & 0x1; + const uint16_t bitJ1 = useJs ? ((result >> 22) & 0x1) : bitS; + const uint16_t bitI1 = (~(bitJ1 ^ bitS)) & 0x1; + const uint16_t resLo = (bitI1 << 13) | (bitI2 << 11) | imm11; + + applyThmReloc(location, resHi, resLo, 0x7FF, 0x2FFF); +} + +/// \brief R_ARM_THM_CALL - ((S + A) | T) - P +static void relocR_ARM_THM_CALL(uint8_t *location, uint64_t P, uint64_t S, + int64_t A, bool useJs, bool addressesThumb) { + uint64_t T = addressesThumb; + const bool switchMode = !addressesThumb; + + if (switchMode) { + P &= ~0x3; // Align(P, 4) by rounding down + } + + uint32_t result = (uint32_t)(((S + A) | T) - P); + + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + relocR_ARM_THM_B_L(location, result, useJs); + + if (switchMode) { + applyThmReloc(location, 0, 0, 0, 0x1001); + } +} + +/// \brief R_ARM_THM_JUMP24 - ((S + A) | T) - P +static void relocR_ARM_THM_JUMP24(uint8_t *location, uint64_t P, uint64_t S, + int64_t A, bool addressesThumb) { + uint64_t T = addressesThumb; + uint32_t result = (uint32_t)(((S + A) | T) - P); + + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + relocR_ARM_THM_B_L(location, result, true); +} + +/// \brief R_ARM_THM_JUMP11 - S + A - P +static void relocR_ARM_THM_JUMP11(uint8_t *location, uint64_t P, uint64_t S, + int64_t A) { + uint32_t result = (uint32_t)(S + A - P); + + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + + //we cut off first bit because it is always 1 according to p. 4.5.3 + result = (result & 0x0FFE) >> 1; + + applyThumb16Reloc(location, result, 0x7FF); +} + +/// \brief R_ARM_CALL - ((S + A) | T) - P +static void relocR_ARM_CALL(uint8_t *location, uint64_t P, uint64_t S, + int64_t A, bool addressesThumb) { + uint64_t T = addressesThumb; + const bool switchMode = addressesThumb; + + uint32_t result = (uint32_t)(((S + A) | T) - P); + const uint32_t imm24 = (result & 0x03FFFFFC) >> 2; + + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + applyArmReloc(location, imm24, 0xFFFFFF); + + if (switchMode) { + const uint32_t bitH = (result & 0x2) >> 1; + applyArmReloc(location, (0xFA | bitH) << 24, 0xFF000000); + } +} + +/// \brief R_ARM_JUMP24 - ((S + A) | T) - P +static void relocR_ARM_JUMP24(uint8_t *location, uint64_t P, uint64_t S, + int64_t A, bool addressesThumb) { + uint64_t T = addressesThumb; + uint32_t result = (uint32_t)(((S + A) | T) - P); + const uint32_t imm24 = (result & 0x03FFFFFC) >> 2; + + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + applyArmReloc(location, imm24, 0xFFFFFF); +} + +/// \brief Relocate ARM MOVW/MOVT instructions +static void relocR_ARM_MOV(uint8_t *location, uint32_t result) { + const uint32_t imm12 = result & 0xFFF; + const uint32_t imm4 = (result >> 12) & 0xF; + + applyArmReloc(location, (imm4 << 16) | imm12, 0xF0FFF); +} + +/// \brief R_ARM_MOVW_ABS_NC - (S + A) | T +static void relocR_ARM_MOVW_ABS_NC(uint8_t *location, uint64_t P, uint64_t S, + int64_t A, bool addressesThumb) { + uint64_t T = addressesThumb; + uint32_t result = (uint32_t)((S + A) | T); + const uint32_t arg = result & 0x0000FFFF; + + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + return relocR_ARM_MOV(location, arg); +} + +/// \brief R_ARM_MOVT_ABS - S + A +static void relocR_ARM_MOVT_ABS(uint8_t *location, uint64_t P, uint64_t S, + int64_t A) { + uint32_t result = (uint32_t)(S + A); + const uint32_t arg = (result & 0xFFFF0000) >> 16; + + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + return relocR_ARM_MOV(location, arg); +} + +/// \brief Relocate Thumb MOVW/MOVT instructions +static void relocR_ARM_THM_MOV(uint8_t *location, uint32_t result) { + const uint16_t imm8 = result & 0xFF; + const uint16_t imm3 = (result >> 8) & 0x7; + const uint16_t resLo = (imm3 << 12) | imm8; + + const uint16_t imm4 = (result >> 12) & 0xF; + const uint16_t bitI = (result >> 11) & 0x1; + const uint16_t resHi = (bitI << 10) | imm4; + + applyThmReloc(location, resHi, resLo, 0x40F, 0x70FF); +} + +/// \brief R_ARM_THM_MOVW_ABS_NC - (S + A) | T +static void relocR_ARM_THM_MOVW_ABS_NC(uint8_t *location, uint64_t P, + uint64_t S, int64_t A, + bool addressesThumb) { + uint64_t T = addressesThumb; + uint32_t result = (uint32_t)((S + A) | T); + const uint32_t arg = result & 0x0000FFFF; + + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " T: 0x" << Twine::utohexstr(T); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + return relocR_ARM_THM_MOV(location, arg); +} + +/// \brief R_ARM_THM_MOVT_ABS - S + A +static void relocR_ARM_THM_MOVT_ABS(uint8_t *location, uint64_t P, uint64_t S, + int64_t A) { + uint32_t result = (uint32_t)(S + A); + const uint32_t arg = (result & 0xFFFF0000) >> 16; + + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + return relocR_ARM_THM_MOV(location, arg); +} + +/// \brief R_ARM_TLS_IE32 - GOT(S) + A - P => S + A - P +static void relocR_ARM_TLS_IE32(uint8_t *location, uint64_t P, uint64_t S, + int64_t A) { + uint32_t result = (uint32_t)(S + A - P); + + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + applyArmReloc(location, result); +} + +/// \brief R_ARM_TLS_LE32 - S + A - tp => S + A + tpoff +static void relocR_ARM_TLS_LE32(uint8_t *location, uint64_t P, uint64_t S, + int64_t A, uint64_t tpoff) { + uint32_t result = (uint32_t)(S + A + tpoff); + + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t\tHandle " << LLVM_FUNCTION_NAME << " -"; + llvm::dbgs() << " S: 0x" << Twine::utohexstr(S); + llvm::dbgs() << " A: 0x" << Twine::utohexstr(A); + llvm::dbgs() << " P: 0x" << Twine::utohexstr(P); + llvm::dbgs() << " result: 0x" << Twine::utohexstr(result) << "\n"); + applyArmReloc(location, result); +} + +std::error_code ARMTargetRelocationHandler::applyRelocation( + ELFWriter &writer, llvm::FileOutputBuffer &buf, const lld::AtomLayout &atom, + const Reference &ref) const { + uint8_t *atomContent = buf.getBufferStart() + atom._fileOffset; + uint8_t *location = atomContent + ref.offsetInAtom(); + uint64_t targetVAddress = writer.addressOfAtom(ref.target()); + uint64_t relocVAddress = atom._virtualAddr + ref.offsetInAtom(); + + if (ref.kindNamespace() != Reference::KindNamespace::ELF) + return std::error_code(); + assert(ref.kindArch() == Reference::KindArch::ARM); + + // Calculate proper initial addend for the relocation + const Reference::Addend addend = + readAddend(location, ref.kindValue()); + + // Flags that the relocation addresses Thumb instruction + bool addressesThumb = false; + + if (const auto *definedAtom = dyn_cast<DefinedAtom>(ref.target())) { + addressesThumb = (DefinedAtom::codeARMThumb == definedAtom->codeModel()); + } + + switch (ref.kindValue()) { + case R_ARM_NONE: + break; + case R_ARM_ABS32: + relocR_ARM_ABS32(location, relocVAddress, targetVAddress, addend, + addressesThumb); + break; + case R_ARM_REL32: + relocR_ARM_REL32(location, relocVAddress, targetVAddress, addend, + addressesThumb); + break; + case R_ARM_THM_CALL: + // TODO: consider adding bool variable to disable J1 & J2 for archs + // before ARMv6 + relocR_ARM_THM_CALL(location, relocVAddress, targetVAddress, addend, true, + addressesThumb); + break; + case R_ARM_CALL: + relocR_ARM_CALL(location, relocVAddress, targetVAddress, addend, + addressesThumb); + break; + case R_ARM_JUMP24: + relocR_ARM_JUMP24(location, relocVAddress, targetVAddress, addend, + addressesThumb); + break; + case R_ARM_THM_JUMP24: + relocR_ARM_THM_JUMP24(location, relocVAddress, targetVAddress, addend, + addressesThumb); + break; + case R_ARM_THM_JUMP11: + relocR_ARM_THM_JUMP11(location, relocVAddress, targetVAddress, addend); + break; + case R_ARM_MOVW_ABS_NC: + relocR_ARM_MOVW_ABS_NC(location, relocVAddress, targetVAddress, addend, + addressesThumb); + break; + case R_ARM_MOVT_ABS: + relocR_ARM_MOVT_ABS(location, relocVAddress, targetVAddress, addend); + break; + case R_ARM_THM_MOVW_ABS_NC: + relocR_ARM_THM_MOVW_ABS_NC(location, relocVAddress, targetVAddress, addend, + addressesThumb); + break; + case R_ARM_THM_MOVT_ABS: + relocR_ARM_THM_MOVT_ABS(location, relocVAddress, targetVAddress, addend); + break; + case R_ARM_PREL31: + relocR_ARM_PREL31(location, relocVAddress, targetVAddress, addend, + addressesThumb); + break; + case R_ARM_TLS_IE32: + relocR_ARM_TLS_IE32(location, relocVAddress, targetVAddress, addend); + break; + case R_ARM_TLS_LE32: + relocR_ARM_TLS_LE32(location, relocVAddress, targetVAddress, addend, + _armLayout.getTPOffset()); + break; + default: + return make_unhandled_reloc_error(); + } + + return std::error_code(); +} diff --git a/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.h b/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.h new file mode 100644 index 0000000000000..227d68617bf98 --- /dev/null +++ b/lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.h @@ -0,0 +1,38 @@ +//===--------- lib/ReaderWriter/ELF/ARM/ARMRelocationHandler.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_ARM_ARM_RELOCATION_HANDLER_H +#define LLD_READER_WRITER_ELF_ARM_ARM_RELOCATION_HANDLER_H + +#include "ARMTargetHandler.h" + +namespace lld { +namespace elf { +typedef llvm::object::ELFType<llvm::support::little, 2, false> ARMELFType; + +template <class ELFT> class ARMTargetLayout; + +class ARMTargetRelocationHandler final + : public TargetRelocationHandler { +public: + ARMTargetRelocationHandler(ARMTargetLayout<ARMELFType> &layout) + : _armLayout(layout) {} + + std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, + const lld::AtomLayout &, + const Reference &) const override; + +private: + ARMTargetLayout<ARMELFType> &_armLayout; +}; + +} // end namespace elf +} // end namespace lld + +#endif // LLD_READER_WRITER_ELF_ARM_ARM_RELOCATION_HANDLER_H diff --git a/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp b/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp new file mode 100644 index 0000000000000..27ec66ac55572 --- /dev/null +++ b/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp @@ -0,0 +1,373 @@ +//===--------- lib/ReaderWriter/ELF/ARM/ARMRelocationPass.cpp -------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Defines the relocation processing pass for ARM. This includes +/// GOT and PLT entries, TLS, COPY, and ifunc. +/// +/// This also includes additional behavior that gnu-ld and gold implement but +/// which is not specified anywhere. +/// +//===----------------------------------------------------------------------===// + +#include "ARMRelocationPass.h" +#include "ARMLinkingContext.h" +#include "Atoms.h" +#include "lld/Core/Simple.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Debug.h" + +using namespace lld; +using namespace lld::elf; +using namespace llvm::ELF; + +// ARM B/BL instructions of static relocation veneer. +// TODO: consider different instruction set for archs below ARMv5 +// (one as for Thumb may be used though it's less optimal). +static const uint8_t Veneer_ARM_B_BL_StaticAtomContent[8] = { + 0x04, 0xf0, 0x1f, 0xe5, // ldr pc, [pc, #-4] + 0x00, 0x00, 0x00, 0x00 // <target_symbol_address> +}; + +// Thumb B/BL instructions of static relocation veneer. +// TODO: consider different instruction set for archs above ARMv5 +// (one as for ARM may be used since it's more optimal). +static const uint8_t Veneer_THM_B_BL_StaticAtomContent[8] = { + 0x78, 0x47, // bx pc + 0x00, 0x00, // nop + 0xfe, 0xff, 0xff, 0xea // b <target_symbol_address> +}; + +// .got values +static const uint8_t ARMGotAtomContent[4] = {0}; + +namespace { +/// \brief Atoms that hold veneer code. +class VeneerAtom : public SimpleELFDefinedAtom { + StringRef _section; + +public: + VeneerAtom(const File &f, StringRef secName) + : SimpleELFDefinedAtom(f), _section(secName) {} + + Scope scope() const override { return DefinedAtom::scopeTranslationUnit; } + + SectionChoice sectionChoice() const override { + return DefinedAtom::sectionBasedOnContent; + } + + StringRef customSectionName() const override { return _section; } + + ContentType contentType() const override { + return DefinedAtom::typeCode; + } + + uint64_t size() const override { return rawContent().size(); } + + ContentPermissions permissions() const override { return permR_X; } + + Alignment alignment() const override { return Alignment(2); } + + StringRef name() const override { return _name; } + std::string _name; +}; + +/// \brief Atoms that hold veneer for statically relocated +/// ARM B/BL instructions. +class Veneer_ARM_B_BL_StaticAtom : public VeneerAtom { +public: + Veneer_ARM_B_BL_StaticAtom(const File &f, StringRef secName) + : VeneerAtom(f, secName) {} + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(Veneer_ARM_B_BL_StaticAtomContent); + } +}; + +/// \brief Atoms that hold veneer for statically relocated +/// Thumb B/BL instructions. +class Veneer_THM_B_BL_StaticAtom : public VeneerAtom { +public: + Veneer_THM_B_BL_StaticAtom(const File &f, StringRef secName) + : VeneerAtom(f, secName) {} + + DefinedAtom::CodeModel codeModel() const override { + return DefinedAtom::codeARMThumb; + } + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(Veneer_THM_B_BL_StaticAtomContent); + } +}; + +/// \brief Atoms that are used by ARM dynamic linking +class ARMGOTAtom : public GOTAtom { +public: + ARMGOTAtom(const File &f, StringRef secName) : GOTAtom(f, secName) {} + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(ARMGotAtomContent); + } + + Alignment alignment() const override { return Alignment(2); } +}; + +class ELFPassFile : public SimpleFile { +public: + ELFPassFile(const ELFLinkingContext &eti) : SimpleFile("ELFPassFile") { + setOrdinal(eti.getNextOrdinalAndIncrement()); + } + + llvm::BumpPtrAllocator _alloc; +}; + +/// \brief CRTP base for handling relocations. +template <class Derived> class ARMRelocationPass : public Pass { + /// \brief Handle a specific reference. + void handleReference(const DefinedAtom &atom, const Reference &ref) { + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "\t" << LLVM_FUNCTION_NAME << "()" + << ": Name of Defined Atom: " << atom.name().str(); + llvm::dbgs() << " kindValue: " << ref.kindValue() << "\n"); + if (ref.kindNamespace() != Reference::KindNamespace::ELF) + return; + assert(ref.kindArch() == Reference::KindArch::ARM); + switch (ref.kindValue()) { + case R_ARM_JUMP24: + case R_ARM_THM_JUMP24: + static_cast<Derived *>(this)->handleVeneer(atom, ref); + break; + case R_ARM_TLS_IE32: + static_cast<Derived *>(this)->handleTLSIE32(ref); + break; + } + } + +protected: + std::error_code handleVeneer(const DefinedAtom &atom, const Reference &ref) { + // Target symbol and relocated place should have different + // instruction sets in order a veneer to be generated in between. + const auto *target = dyn_cast<DefinedAtom>(ref.target()); + if (!target || target->codeModel() == atom.codeModel()) + return std::error_code(); + + // TODO: For unconditional jump instructions (R_ARM_CALL and R_ARM_THM_CALL) + // fixup isn't possible without veneer generation for archs below ARMv5. + + // Veneers may only be generated for STT_FUNC target symbols + // or for symbols located in sections different to the place of relocation. + const auto kindValue = ref.kindValue(); + StringRef secName = atom.customSectionName(); + if (DefinedAtom::typeCode != target->contentType() && + !target->customSectionName().equals(secName)) { + StringRef kindValStr; + if (!this->_ctx.registry().referenceKindToString( + ref.kindNamespace(), ref.kindArch(), kindValue, kindValStr)) { + kindValStr = "unknown"; + } + + std::string errStr = + (Twine("Reference of type ") + Twine(kindValue) + " (" + kindValStr + + ") from " + atom.name() + "+" + Twine(ref.offsetInAtom()) + " to " + + ref.target()->name() + "+" + Twine(ref.addend()) + + " cannot be effected without a veneer").str(); + + llvm_unreachable(errStr.c_str()); + } + + const Atom *veneer = nullptr; + switch (kindValue) { + case R_ARM_JUMP24: + veneer = static_cast<Derived *>(this) + ->getVeneer_ARM_B_BL(target, secName); + break; + case R_ARM_THM_JUMP24: + veneer = static_cast<Derived *>(this) + ->getVeneer_THM_B_BL(target, secName); + break; + default: + llvm_unreachable("Unhandled reference type for veneer generation"); + } + + assert(veneer && "The veneer is not set"); + const_cast<Reference &>(ref).setTarget(veneer); + return std::error_code(); + } + + std::error_code handleTLSIE32(const Reference &ref) { + if (const auto *target = dyn_cast<DefinedAtom>(ref.target())) { + const_cast<Reference &>(ref).setTarget( + static_cast<Derived *>(this)->getTLSTPOFF32(target)); + return std::error_code(); + } + llvm_unreachable("R_ARM_TLS_IE32 reloc targets wrong atom type"); + } + + /// \brief Create a GOT entry for TLS with reloc type and addend specified. + template <Reference::KindValue R_ARM_TLS, Reference::Addend A = 0> + const GOTAtom *getGOTTLSEntry(const DefinedAtom *da) { + auto got = _gotMap.find(da); + if (got != _gotMap.end()) + return got->second; + auto g = new (_file._alloc) ARMGOTAtom(_file, ".got"); + g->addReferenceELF_ARM(R_ARM_TLS, 0, da, A); +#ifndef NDEBUG + g->_name = "__got_tls_"; + g->_name += da->name(); +#endif + _gotMap[da] = g; + _gotVector.push_back(g); + return g; + } + +public: + ARMRelocationPass(const ELFLinkingContext &ctx) : _file(ctx), _ctx(ctx) {} + + /// \brief Do the pass. + /// + /// The goal here is to first process each reference individually. Each call + /// to handleReference may modify the reference itself and/or create new + /// atoms which must be stored in one of the maps below. + /// + /// After all references are handled, the atoms created during that are all + /// added to mf. + void perform(std::unique_ptr<MutableFile> &mf) override { + ScopedTask task(getDefaultDomain(), "ARM GOT/PLT Pass"); + DEBUG_WITH_TYPE( + "ARM", llvm::dbgs() << "Undefined Atoms" << "\n"; + for (const auto &atom + : mf->undefined()) { + llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; + } + + llvm::dbgs() << "Shared Library Atoms" << "\n"; + for (const auto &atom + : mf->sharedLibrary()) { + llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; + } + + llvm::dbgs() << "Absolute Atoms" << "\n"; + for (const auto &atom + : mf->absolute()) { + llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; + } + + llvm::dbgs() << "Defined Atoms" << "\n"; + for (const auto &atom + : mf->defined()) { + llvm::dbgs() << " Name of Atom: " << atom->name().str() << "\n"; + }); + + // Process all references. + for (const auto &atom : mf->defined()) { + for (const auto &ref : *atom) { + handleReference(*atom, *ref); + } + } + + // Add all created atoms to the link. + uint64_t ordinal = 0; + for (auto &got : _gotVector) { + got->setOrdinal(ordinal++); + mf->addAtom(*got); + } + for (auto &veneer : _veneerVector) { + veneer->setOrdinal(ordinal++); + mf->addAtom(*veneer); + } + } + +protected: + /// \brief Owner of all the Atoms created by this pass. + ELFPassFile _file; + const ELFLinkingContext &_ctx; + + /// \brief Map Atoms to their GOT entries. + llvm::DenseMap<const Atom *, GOTAtom *> _gotMap; + + /// \brief Map Atoms to their veneers. + llvm::DenseMap<const Atom *, VeneerAtom *> _veneerMap; + + /// \brief the list of GOT/PLT atoms + std::vector<GOTAtom *> _gotVector; + + /// \brief the list of veneer atoms. + std::vector<VeneerAtom *> _veneerVector; +}; + +/// This implements the static relocation model. Meaning GOT and PLT entries are +/// not created for references that can be directly resolved. These are +/// converted to a direct relocation. For entries that do require a GOT or PLT +/// entry, that entry is statically bound. +/// +/// TLS always assumes module 1 and attempts to remove indirection. +class ARMStaticRelocationPass final + : public ARMRelocationPass<ARMStaticRelocationPass> { +public: + ARMStaticRelocationPass(const elf::ARMLinkingContext &ctx) + : ARMRelocationPass(ctx) {} + + /// \brief Get the veneer for ARM B/BL instructions. + const VeneerAtom *getVeneer_ARM_B_BL(const DefinedAtom *da, + StringRef secName) { + auto veneer = _veneerMap.find(da); + if (_veneerMap.end() != veneer) + return veneer->second; + + auto v = new (_file._alloc) Veneer_ARM_B_BL_StaticAtom(_file, secName); + v->addReferenceELF_ARM(R_ARM_ABS32, 4, da, 0); + + v->_name = "__"; + v->_name += da->name(); + v->_name += "_from_arm"; + + _veneerMap[da] = v; + _veneerVector.push_back(v); + return v; + } + + /// \brief Get the veneer for Thumb B/BL instructions. + const VeneerAtom *getVeneer_THM_B_BL(const DefinedAtom *da, + StringRef secName) { + auto veneer = _veneerMap.find(da); + if (_veneerMap.end() != veneer) + return veneer->second; + + auto v = new (_file._alloc) Veneer_THM_B_BL_StaticAtom(_file, secName); + v->addReferenceELF_ARM(R_ARM_JUMP24, 4, da, 0); + + v->_name = "__"; + v->_name += da->name(); + v->_name += "_from_thumb"; + + _veneerMap[da] = v; + _veneerVector.push_back(v); + return v; + } + + /// \brief Create a GOT entry for R_ARM_TLS_TPOFF32 reloc. + const GOTAtom *getTLSTPOFF32(const DefinedAtom *da) { + return getGOTTLSEntry<R_ARM_TLS_LE32>(da); + } +}; + +} // end of anon namespace + +std::unique_ptr<Pass> +lld::elf::createARMRelocationPass(const ARMLinkingContext &ctx) { + switch (ctx.getOutputELFType()) { + case llvm::ELF::ET_EXEC: + if (ctx.isDynamic()) + llvm_unreachable("Unhandled output file type"); + return llvm::make_unique<ARMStaticRelocationPass>(ctx); + default: + llvm_unreachable("Unhandled output file type"); + } +} diff --git a/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.h b/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.h new file mode 100644 index 0000000000000..651e798f33b1b --- /dev/null +++ b/lib/ReaderWriter/ELF/ARM/ARMRelocationPass.h @@ -0,0 +1,31 @@ +//===--------- lib/ReaderWriter/ELF/ARM/ARMRelocationPass.h ---------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Declares the relocation processing pass for ARM. This includes +/// GOT and PLT entries, TLS, COPY, and ifunc. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_ELF_ARM_ARM_RELOCATION_PASS_H +#define LLD_READER_WRITER_ELF_ARM_ARM_RELOCATION_PASS_H + +#include <memory> + +namespace lld { +class Pass; +namespace elf { +class ARMLinkingContext; + +/// \brief Create ARM relocation pass for the given linking context. +std::unique_ptr<Pass> createARMRelocationPass(const ARMLinkingContext &); +} +} + +#endif diff --git a/lib/ReaderWriter/ELF/ARM/ARMSymbolTable.h b/lib/ReaderWriter/ELF/ARM/ARMSymbolTable.h new file mode 100644 index 0000000000000..540a480421a8a --- /dev/null +++ b/lib/ReaderWriter/ELF/ARM/ARMSymbolTable.h @@ -0,0 +1,46 @@ +//===--------- lib/ReaderWriter/ELF/ARM/ARMSymbolTable.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_ARM_ARM_SYMBOL_TABLE_H +#define LLD_READER_WRITER_ELF_ARM_ARM_SYMBOL_TABLE_H + +namespace lld { +namespace elf { + +/// \brief The SymbolTable class represents the symbol table in a ELF file +template<class ELFT> +class ARMSymbolTable : public SymbolTable<ELFT> { +public: + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + + ARMSymbolTable(const ELFLinkingContext &context); + + void addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, + int64_t addr) override; +}; + +template <class ELFT> +ARMSymbolTable<ELFT>::ARMSymbolTable(const ELFLinkingContext &context) + : SymbolTable<ELFT>(context, ".symtab", + DefaultLayout<ELFT>::ORDER_SYMBOL_TABLE) {} + +template <class ELFT> +void ARMSymbolTable<ELFT>::addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, + int64_t addr) { + SymbolTable<ELFT>::addDefinedAtom(sym, da, addr); + + // Set zero bit to distinguish symbols addressing Thumb instructions + if (DefinedAtom::codeARMThumb == da->codeModel()) + sym.st_value = static_cast<int64_t>(sym.st_value) | 0x1; +} + +} // elf +} // lld + +#endif // LLD_READER_WRITER_ELF_ARM_ARM_SYMBOL_TABLE_H diff --git a/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.cpp b/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.cpp new file mode 100644 index 0000000000000..de90f490f621b --- /dev/null +++ b/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.cpp @@ -0,0 +1,44 @@ +//===--------- lib/ReaderWriter/ELF/ARM/ARMTargetHandler.cpp --------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Atoms.h" +#include "ARMExecutableWriter.h" +#include "ARMTargetHandler.h" +#include "ARMLinkingContext.h" + +using namespace lld; +using namespace elf; + +ARMTargetHandler::ARMTargetHandler(ARMLinkingContext &context) + : _context(context), _armTargetLayout( + new ARMTargetLayout<ARMELFType>(context)), + _armRelocationHandler(new ARMTargetRelocationHandler( + *_armTargetLayout.get())) {} + +void ARMTargetHandler::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, Reference::KindArch::ARM, + kindStrings); +} + +std::unique_ptr<Writer> ARMTargetHandler::getWriter() { + switch (this->_context.getOutputELFType()) { + case llvm::ELF::ET_EXEC: + return std::unique_ptr<Writer>( + new ARMExecutableWriter<ARMELFType>(_context, *_armTargetLayout.get())); + default: + llvm_unreachable("unsupported output type"); + } +} + +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), + +const Registry::KindStrings ARMTargetHandler::kindStrings[] = { +#include "llvm/Support/ELFRelocs/ARM.def" + LLD_KIND_STRING_END +}; diff --git a/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.h b/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.h new file mode 100644 index 0000000000000..10641954da25d --- /dev/null +++ b/lib/ReaderWriter/ELF/ARM/ARMTargetHandler.h @@ -0,0 +1,88 @@ +//===--------- lib/ReaderWriter/ELF/ARM/ARMTargetHandler.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_ARM_ARM_TARGET_HANDLER_H +#define LLD_READER_WRITER_ELF_ARM_ARM_TARGET_HANDLER_H + +#include "ARMELFFile.h" +#include "ARMELFReader.h" +#include "ARMRelocationHandler.h" +#include "DefaultTargetHandler.h" +#include "TargetLayout.h" + +#include "lld/Core/Simple.h" +#include "llvm/ADT/Optional.h" +#include <map> + +namespace lld { +namespace elf { +typedef llvm::object::ELFType<llvm::support::little, 2, false> ARMELFType; +class ARMLinkingContext; + +template <class ELFT> class ARMTargetLayout : public TargetLayout<ELFT> { +public: + ARMTargetLayout(ARMLinkingContext &context) + : TargetLayout<ELFT>(context) {} + + uint64_t getTPOffset() { + if (_tpOff.hasValue()) + return *_tpOff; + + for (const auto &phdr : *this->_programHeader) { + if (phdr->p_type == llvm::ELF::PT_TLS) { + _tpOff = llvm::RoundUpToAlignment(TCB_SIZE, phdr->p_align); + return *_tpOff; + } + } + llvm_unreachable("TLS segment not found"); + } + +private: + // TCB block size of the TLS. + enum { TCB_SIZE = 0x8 }; + + // Cached value of the TLS offset from the $tp pointer. + llvm::Optional<uint64_t> _tpOff; +}; + +class ARMTargetHandler final : public DefaultTargetHandler<ARMELFType> { +public: + ARMTargetHandler(ARMLinkingContext &context); + + ARMTargetLayout<ARMELFType> &getTargetLayout() override { + return *(_armTargetLayout.get()); + } + + void registerRelocationNames(Registry ®istry) override; + + const ARMTargetRelocationHandler &getRelocationHandler() const override { + return *(_armRelocationHandler.get()); + } + + std::unique_ptr<Reader> getObjReader() override { + return std::unique_ptr<Reader>(new ARMELFObjectReader(_context)); + } + + std::unique_ptr<Reader> getDSOReader() override { + return std::unique_ptr<Reader>(new ARMELFDSOReader(_context)); + } + + std::unique_ptr<Writer> getWriter() override; + +private: + static const Registry::KindStrings kindStrings[]; + ARMLinkingContext &_context; + std::unique_ptr<ARMTargetLayout<ARMELFType>> _armTargetLayout; + std::unique_ptr<ARMTargetRelocationHandler> _armRelocationHandler; +}; + +} // end namespace elf +} // end namespace lld + +#endif // LLD_READER_WRITER_ELF_ARM_ARM_TARGET_HANDLER_H diff --git a/lib/ReaderWriter/ELF/ARM/CMakeLists.txt b/lib/ReaderWriter/ELF/ARM/CMakeLists.txt new file mode 100644 index 0000000000000..2ccf9eb6266db --- /dev/null +++ b/lib/ReaderWriter/ELF/ARM/CMakeLists.txt @@ -0,0 +1,12 @@ +add_llvm_library(lldARMELFTarget + ARMLinkingContext.cpp + ARMTargetHandler.cpp + ARMRelocationHandler.cpp + ARMRelocationPass.cpp + LINK_LIBS + lldELF + lldReaderWriter + lldCore + LLVMObject + LLVMSupport + ) diff --git a/lib/ReaderWriter/ELF/ARM/Makefile b/lib/ReaderWriter/ELF/ARM/Makefile new file mode 100644 index 0000000000000..f67d36a1b612d --- /dev/null +++ b/lib/ReaderWriter/ELF/ARM/Makefile @@ -0,0 +1,15 @@ +##===------ lld/lib/ReaderWriter/ELF/ARM/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 := lldARMELFTarget +USEDLIBS = lldCore.a +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF/ARM -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/ARM/TODO.rst b/lib/ReaderWriter/ELF/ARM/TODO.rst new file mode 100644 index 0000000000000..d05419decb786 --- /dev/null +++ b/lib/ReaderWriter/ELF/ARM/TODO.rst @@ -0,0 +1,20 @@ +ELF ARM +~~~~~~~~~~~ + +Unimplemented Features +###################### + +* Static executable linking - in progress +* Dynamic executable linking +* DSO linking +* PLT entries' generation for images larger than 2^28 bytes (see Sec. A.3 of the ELF reference) +* ARM and Thumb interworking (see http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0203j/Bcghfebi.html) +* .ARM.exidx section handling +* -init/-fini options +* Lots of relocations + +Unimplemented Relocations +######################### + +All of these relocations are defined in: +http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044e/IHI0044E_aaelf.pdf diff --git a/lib/ReaderWriter/ELF/Atoms.h b/lib/ReaderWriter/ELF/Atoms.h new file mode 100644 index 0000000000000..6a506d21d9385 --- /dev/null +++ b/lib/ReaderWriter/ELF/Atoms.h @@ -0,0 +1,849 @@ +//===- lib/ReaderWriter/ELF/Atoms.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_ATOMS_H +#define LLD_READER_WRITER_ELF_ATOMS_H + +#include "TargetHandler.h" +#include "lld/Core/LLVM.h" +#include "lld/Core/Simple.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringSwitch.h" +#include <memory> +#include <vector> + +namespace lld { +namespace elf { +template <class ELFT> class DynamicFile; +template <typename ELFT> class ELFFile; + +/// \brief Relocation References: Defined Atoms may contain references that will +/// need to be patched before the executable is written. +/// +/// Construction of ELFReferences is two pass process. ELFReferences are +/// instantiated while we are iterating over symbol tables to atomize +/// symbols. At that time we only know the index of relocation target symbol +/// (not target atom) about a relocation, so we store the index to +/// ELFREference. In the second pass, ELFReferences are revisited to update +/// target atoms by target symbol indexes. +template <class ELFT> class ELFReference : public Reference { + typedef llvm::object::Elf_Rel_Impl<ELFT, false> Elf_Rel; + typedef llvm::object::Elf_Rel_Impl<ELFT, true> Elf_Rela; + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + +public: + ELFReference(const Elf_Rela *rela, uint64_t off, Reference::KindArch arch, + Reference::KindValue relocType, uint32_t idx) + : Reference(Reference::KindNamespace::ELF, arch, relocType), + _target(nullptr), _targetSymbolIndex(idx), _offsetInAtom(off), + _addend(rela->r_addend) {} + + ELFReference(uint64_t off, Reference::KindArch arch, + Reference::KindValue relocType, uint32_t idx) + : Reference(Reference::KindNamespace::ELF, arch, relocType), + _target(nullptr), _targetSymbolIndex(idx), _offsetInAtom(off), + _addend(0) {} + + ELFReference(uint32_t edgeKind) + : Reference(Reference::KindNamespace::all, Reference::KindArch::all, + edgeKind), + _target(nullptr), _targetSymbolIndex(0), _offsetInAtom(0), _addend(0) {} + + uint64_t offsetInAtom() const override { return _offsetInAtom; } + + const Atom *target() const override { return _target; } + + /// \brief The symbol table index that contains the target reference. + uint64_t targetSymbolIndex() const { + return _targetSymbolIndex; + } + + Addend addend() const override { return _addend; } + + virtual void setOffset(uint64_t off) { _offsetInAtom = off; } + + void setAddend(Addend A) override { _addend = A; } + + void setTarget(const Atom *newAtom) override { _target = newAtom; } + +private: + const Atom *_target; + uint64_t _targetSymbolIndex; + uint64_t _offsetInAtom; + Addend _addend; +}; + +/// \brief These atoms store symbols that are fixed to a particular address. +/// This atom has no content its address will be used by the writer to fixup +/// references that point to it. +template <class ELFT> class ELFAbsoluteAtom : public AbsoluteAtom { + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + +public: + ELFAbsoluteAtom(const ELFFile<ELFT> &file, StringRef name, + const Elf_Sym *symbol, uint64_t value) + : _owningFile(file), _name(name), _symbol(symbol), _value(value) { + } + + const ELFFile<ELFT> &file() const override { return _owningFile; } + + Scope scope() const override { + if (_symbol->getVisibility() == llvm::ELF::STV_HIDDEN) + return scopeLinkageUnit; + if (_symbol->getBinding() == llvm::ELF::STB_LOCAL) + return scopeTranslationUnit; + return scopeGlobal; + } + + StringRef name() const override { return _name; } + + uint64_t value() const override { return _value; } + +private: + const ELFFile<ELFT> &_owningFile; + StringRef _name; + const Elf_Sym *_symbol; + uint64_t _value; +}; + +/// \brief ELFUndefinedAtom: These atoms store undefined symbols and are place +/// holders that will be replaced by defined atoms later in the linking process. +template <class ELFT> class ELFUndefinedAtom : public lld::UndefinedAtom { + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + +public: + ELFUndefinedAtom(const File &file, StringRef name, const Elf_Sym *symbol) + : _owningFile(file), _name(name), _symbol(symbol) {} + + const File &file() const override { return _owningFile; } + + StringRef name() const override { return _name; } + + // A symbol in ELF can be undefined at build time if the symbol is a undefined + // weak symbol. + CanBeNull canBeNull() const override { + if (_symbol->getBinding() == llvm::ELF::STB_WEAK) + return CanBeNull::canBeNullAtBuildtime; + return CanBeNull::canBeNullNever; + } + +private: + const File &_owningFile; + StringRef _name; + const Elf_Sym *_symbol; +}; + +/// \brief This atom stores defined symbols and will contain either data or +/// code. +template <class ELFT> class ELFDefinedAtom : public DefinedAtom { + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + +public: + ELFDefinedAtom(const ELFFile<ELFT> &file, StringRef symbolName, + StringRef sectionName, const Elf_Sym *symbol, + const Elf_Shdr *section, ArrayRef<uint8_t> contentData, + unsigned int referenceStart, unsigned int referenceEnd, + std::vector<ELFReference<ELFT> *> &referenceList) + : _owningFile(file), _symbolName(symbolName), _sectionName(sectionName), + _symbol(symbol), _section(section), _contentData(contentData), + _referenceStartIndex(referenceStart), _referenceEndIndex(referenceEnd), + _referenceList(referenceList), _contentType(typeUnknown), + _permissions(permUnknown) {} + + ~ELFDefinedAtom() {} + + const ELFFile<ELFT> &file() const override { return _owningFile; } + + StringRef name() const override { return _symbolName; } + + uint64_t ordinal() const override { return _ordinal; } + + const Elf_Sym *symbol() const { return _symbol; } + + const Elf_Shdr *section() const { return _section; } + + uint64_t size() const override { + // Common symbols are not allocated in object files, + // so use st_size to tell how many bytes are required. + if (_symbol && (_symbol->getType() == llvm::ELF::STT_COMMON || + _symbol->st_shndx == llvm::ELF::SHN_COMMON)) + return (uint64_t) _symbol->st_size; + + return _contentData.size(); + } + + Scope scope() const override { + if (!_symbol) + return scopeGlobal; + if (_symbol->getVisibility() == llvm::ELF::STV_HIDDEN) + return scopeLinkageUnit; + if (_symbol->getBinding() != llvm::ELF::STB_LOCAL) + return scopeGlobal; + return scopeTranslationUnit; + } + + // FIXME: Need to revisit this in future. + Interposable interposable() const override { return interposeNo; } + + Merge merge() const override { + if (!_symbol) + return mergeNo; + + if (_symbol->getBinding() == llvm::ELF::STB_WEAK) + return mergeAsWeak; + + if ((_symbol->getType() == llvm::ELF::STT_COMMON) || + _symbol->st_shndx == llvm::ELF::SHN_COMMON) + return mergeAsTentative; + + return mergeNo; + } + + ContentType contentType() const override { + if (_contentType != typeUnknown) + return _contentType; + + ContentType ret = typeUnknown; + uint64_t flags = _section->sh_flags; + + if (_section->sh_type == llvm::ELF::SHT_GROUP) + return typeGroupComdat; + + if (!_symbol && _sectionName.startswith(".gnu.linkonce")) + return typeGnuLinkOnce; + + if (!(flags & llvm::ELF::SHF_ALLOC)) + return _contentType = typeNoAlloc; + + if (_section->sh_flags == + (llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE | llvm::ELF::SHF_TLS)) { + return _contentType = _section->sh_type == llvm::ELF::SHT_NOBITS ? typeThreadZeroFill + : typeThreadData; + } + + if ((_section->sh_flags == llvm::ELF::SHF_ALLOC) && + (_section->sh_type == llvm::ELF::SHT_PROGBITS)) + return _contentType = typeConstant; + + if (_symbol->getType() == llvm::ELF::STT_GNU_IFUNC) + return _contentType = typeResolver; + + if (_symbol->st_shndx == llvm::ELF::SHN_COMMON) + return _contentType = typeZeroFill; + + switch (_section->sh_type) { + case llvm::ELF::SHT_PROGBITS: + flags &= ~llvm::ELF::SHF_ALLOC; + flags &= ~llvm::ELF::SHF_GROUP; + switch (flags) { + case llvm::ELF::SHF_EXECINSTR: + case (llvm::ELF::SHF_WRITE|llvm::ELF::SHF_EXECINSTR): + ret = typeCode; + break; + case llvm::ELF::SHF_WRITE: + ret = typeData; + break; + case (llvm::ELF::SHF_MERGE|llvm::ELF::SHF_STRINGS): + case llvm::ELF::SHF_STRINGS: + case llvm::ELF::SHF_MERGE: + ret = typeConstant; + break; + default: + ret = typeCode; + break; + } + break; + case llvm::ELF::SHT_NOTE: + flags &= ~llvm::ELF::SHF_ALLOC; + switch (flags) { + case llvm::ELF::SHF_WRITE: + ret = typeRWNote; + break; + default: + ret = typeRONote; + break; + } + break; + case llvm::ELF::SHT_NOBITS: + ret = typeZeroFill; + break; + case llvm::ELF::SHT_NULL: + if ((_symbol->getType() == llvm::ELF::STT_COMMON) + || _symbol->st_shndx == llvm::ELF::SHN_COMMON) + ret = typeZeroFill; + break; + case llvm::ELF::SHT_INIT_ARRAY: + case llvm::ELF::SHT_FINI_ARRAY: + ret = typeData; + break; + } + + return _contentType = ret; + } + + Alignment alignment() const override { + if (!_symbol) + return Alignment(0); + + // Obtain proper value of st_value field. + const auto symValue = getSymbolValue(_symbol); + + // Unallocated common symbols specify their alignment constraints in + // st_value. + if ((_symbol->getType() == llvm::ELF::STT_COMMON) || + _symbol->st_shndx == llvm::ELF::SHN_COMMON) { + return Alignment(llvm::Log2_64(symValue)); + } + if (_section->sh_addralign == 0) { + // sh_addralign of 0 means no alignment + return Alignment(0, symValue); + } + return Alignment(llvm::Log2_64(_section->sh_addralign), + symValue % _section->sh_addralign); + } + + // Do we have a choice for ELF? All symbols live in explicit sections. + SectionChoice sectionChoice() const override { + switch (contentType()) { + case typeCode: + case typeData: + case typeZeroFill: + case typeThreadZeroFill: + case typeThreadData: + case typeConstant: + if ((_sectionName == ".text") || (_sectionName == ".data") || + (_sectionName == ".bss") || (_sectionName == ".rodata") || + (_sectionName == ".tdata") || (_sectionName == ".tbss")) + return sectionBasedOnContent; + default: + break; + } + return sectionCustomRequired; + } + + StringRef customSectionName() const override { + if ((contentType() == typeZeroFill) || + (_symbol && _symbol->st_shndx == llvm::ELF::SHN_COMMON)) + return ".bss"; + return _sectionName; + } + + // It isn't clear that __attribute__((used)) is transmitted to the ELF object + // file. + DeadStripKind deadStrip() const override { return deadStripNormal; } + + ContentPermissions permissions() const override { + if (_permissions != permUnknown) + return _permissions; + + uint64_t flags = _section->sh_flags; + + if (!(flags & llvm::ELF::SHF_ALLOC)) + return _permissions = perm___; + + switch (_section->sh_type) { + // permRW_L is for sections modified by the runtime + // loader. + case llvm::ELF::SHT_REL: + case llvm::ELF::SHT_RELA: + return _permissions = permRW_L; + + case llvm::ELF::SHT_DYNAMIC: + case llvm::ELF::SHT_PROGBITS: + case llvm::ELF::SHT_NOTE: + flags &= ~llvm::ELF::SHF_ALLOC; + flags &= ~llvm::ELF::SHF_GROUP; + switch (flags) { + // Code + case llvm::ELF::SHF_EXECINSTR: + return _permissions = permR_X; + case (llvm::ELF::SHF_WRITE|llvm::ELF::SHF_EXECINSTR): + return _permissions = permRWX; + // Data + case llvm::ELF::SHF_WRITE: + return _permissions = permRW_; + // Strings + case llvm::ELF::SHF_MERGE: + case llvm::ELF::SHF_STRINGS: + return _permissions = permR__; + + default: + if (flags & llvm::ELF::SHF_WRITE) + return _permissions = permRW_; + return _permissions = permR__; + } + + case llvm::ELF::SHT_NOBITS: + return _permissions = permRW_; + + case llvm::ELF::SHT_INIT_ARRAY: + case llvm::ELF::SHT_FINI_ARRAY: + return _permissions = permRW_; + + default: + return _permissions = perm___; + } + } + + ArrayRef<uint8_t> rawContent() const override { return _contentData; } + + DefinedAtom::reference_iterator begin() const override { + uintptr_t index = _referenceStartIndex; + const void *it = reinterpret_cast<const void*>(index); + return reference_iterator(*this, it); + } + + DefinedAtom::reference_iterator end() const override { + uintptr_t index = _referenceEndIndex; + const void *it = reinterpret_cast<const void*>(index); + return reference_iterator(*this, it); + } + + const Reference *derefIterator(const void *It) const override { + uintptr_t index = reinterpret_cast<uintptr_t>(It); + assert(index >= _referenceStartIndex); + assert(index < _referenceEndIndex); + return ((_referenceList)[index]); + } + + void incrementIterator(const void *&It) const override { + uintptr_t index = reinterpret_cast<uintptr_t>(It); + ++index; + It = reinterpret_cast<const void *>(index); + } + + void addReference(ELFReference<ELFT> *reference) { + _referenceList.push_back(reference); + _referenceEndIndex = _referenceList.size(); + } + + virtual void setOrdinal(uint64_t ord) { _ordinal = ord; } + +protected: + /// Returns correct st_value for the symbol depending on the architecture. + /// For most architectures it's just a regular st_value with no changes. + virtual uint64_t getSymbolValue(const Elf_Sym *symbol) const { + return symbol->st_value; + } + +protected: + const ELFFile<ELFT> &_owningFile; + StringRef _symbolName; + StringRef _sectionName; + const Elf_Sym *_symbol; + const Elf_Shdr *_section; + /// \brief Holds the bits that make up the atom. + ArrayRef<uint8_t> _contentData; + + uint64_t _ordinal; + unsigned int _referenceStartIndex; + unsigned int _referenceEndIndex; + std::vector<ELFReference<ELFT> *> &_referenceList; + mutable ContentType _contentType; + mutable ContentPermissions _permissions; +}; + +/// \brief This atom stores mergeable Strings +template <class ELFT> class ELFMergeAtom : public DefinedAtom { + typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + +public: + ELFMergeAtom(const ELFFile<ELFT> &file, StringRef sectionName, + const Elf_Shdr *section, ArrayRef<uint8_t> contentData, + uint64_t offset) + : _owningFile(file), _sectionName(sectionName), _section(section), + _contentData(contentData), _offset(offset) { + } + + const ELFFile<ELFT> &file() const override { return _owningFile; } + + StringRef name() const override { return ""; } + + virtual uint64_t section() const { return _section->sh_name; } + + virtual uint64_t offset() const { return _offset; } + + virtual void setOrdinal(uint64_t ord) { _ordinal = ord; } + + uint64_t ordinal() const override { return _ordinal; } + + uint64_t size() const override { return _contentData.size(); } + + Scope scope() const override { return scopeTranslationUnit; } + + Interposable interposable() const override { return interposeNo; } + + Merge merge() const override { return mergeByContent; } + + ContentType contentType() const override { return typeConstant; } + + Alignment alignment() const override { + return Alignment(llvm::Log2_64(_section->sh_addralign)); + } + + SectionChoice sectionChoice() const override { return sectionCustomRequired; } + + StringRef customSectionName() const override { return _sectionName; } + + DeadStripKind deadStrip() const override { return deadStripNormal; } + + ContentPermissions permissions() const override { return permR__; } + + ArrayRef<uint8_t> rawContent() const override { return _contentData; } + + DefinedAtom::reference_iterator begin() const override { + uintptr_t index = 0; + const void *it = reinterpret_cast<const void *>(index); + return reference_iterator(*this, it); + } + + DefinedAtom::reference_iterator end() const override { + uintptr_t index = 0; + const void *it = reinterpret_cast<const void *>(index); + return reference_iterator(*this, it); + } + + const Reference *derefIterator(const void *It) const override { + return nullptr; + } + + void incrementIterator(const void *&It) const override {} + +private: + + const ELFFile<ELFT> &_owningFile; + StringRef _sectionName; + const Elf_Shdr *_section; + /// \brief Holds the bits that make up the atom. + ArrayRef<uint8_t> _contentData; + uint64_t _ordinal; + uint64_t _offset; +}; + +template <class ELFT> class ELFCommonAtom : public DefinedAtom { + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; +public: + ELFCommonAtom(const ELFFile<ELFT> &file, + StringRef symbolName, + const Elf_Sym *symbol) + : _owningFile(file), + _symbolName(symbolName), + _symbol(symbol) {} + + const ELFFile<ELFT> &file() const override { return _owningFile; } + + StringRef name() const override { return _symbolName; } + + uint64_t ordinal() const override { return _ordinal; } + + virtual void setOrdinal(uint64_t ord) { _ordinal = ord; } + + uint64_t size() const override { return _symbol->st_size; } + + Scope scope() const override { + if (_symbol->getVisibility() == llvm::ELF::STV_HIDDEN) + return scopeLinkageUnit; + if (_symbol->getBinding() != llvm::ELF::STB_LOCAL) + return scopeGlobal; + return scopeTranslationUnit; + } + + Interposable interposable() const override { return interposeNo; } + + Merge merge() const override { return mergeAsTentative; } + + ContentType contentType() const override { return typeZeroFill; } + + Alignment alignment() const override { + return Alignment(llvm::Log2_64(_symbol->st_value)); + } + + SectionChoice sectionChoice() const override { return sectionBasedOnContent; } + + StringRef customSectionName() const override { return ".bss"; } + + DeadStripKind deadStrip() const override { return deadStripNormal; } + + ContentPermissions permissions() const override { return permRW_; } + + ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } + + DefinedAtom::reference_iterator begin() const override { + uintptr_t index = 0; + const void *it = reinterpret_cast<const void *>(index); + return reference_iterator(*this, it); + } + + DefinedAtom::reference_iterator end() const override { + uintptr_t index = 0; + const void *it = reinterpret_cast<const void *>(index); + return reference_iterator(*this, it); + } + +protected: + const Reference *derefIterator(const void *iter) const override { + return nullptr; + } + + void incrementIterator(const void *&iter) const override {} + + const ELFFile<ELFT> &_owningFile; + StringRef _symbolName; + const Elf_Sym *_symbol; + uint64_t _ordinal; +}; + +/// \brief An atom from a shared library. +template <class ELFT> class ELFDynamicAtom : public SharedLibraryAtom { + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + +public: + ELFDynamicAtom(const DynamicFile<ELFT> &file, StringRef symbolName, + StringRef loadName, const Elf_Sym *symbol) + : _owningFile(file), _symbolName(symbolName), _loadName(loadName), + _symbol(symbol) { + } + + const DynamicFile<ELFT> &file() const override { return _owningFile; } + + StringRef name() const override { return _symbolName; } + + virtual Scope scope() const { + if (_symbol->getVisibility() == llvm::ELF::STV_HIDDEN) + return scopeLinkageUnit; + if (_symbol->getBinding() != llvm::ELF::STB_LOCAL) + return scopeGlobal; + return scopeTranslationUnit; + } + + StringRef loadName() const override { return _loadName; } + + bool canBeNullAtRuntime() const override { + return _symbol->getBinding() == llvm::ELF::STB_WEAK; + } + + Type type() const override { + switch (_symbol->getType()) { + case llvm::ELF::STT_FUNC: + case llvm::ELF::STT_GNU_IFUNC: + return Type::Code; + case llvm::ELF::STT_OBJECT: + return Type::Data; + default: + return Type::Unknown; + } + } + + uint64_t size() const override { + return _symbol->st_size; + } + +private: + + const DynamicFile<ELFT> &_owningFile; + StringRef _symbolName; + StringRef _loadName; + const Elf_Sym *_symbol; +}; + +class SimpleELFDefinedAtom : public SimpleDefinedAtom { +public: + SimpleELFDefinedAtom(const File &f) : SimpleDefinedAtom(f) {} + + void addReferenceELF(Reference::KindArch arch, Reference::KindValue kindValue, + uint64_t off, const Atom *target, + Reference::Addend addend) { + this->addReference(Reference::KindNamespace::ELF, arch, kindValue, off, + target, addend); + } + + void addReferenceELF_Hexagon(Reference::KindValue relocType, uint64_t off, + const Atom *t, Reference::Addend a) { + this->addReferenceELF(Reference::KindArch::Hexagon, relocType, off, t, a); + } + + void addReferenceELF_x86_64(Reference::KindValue relocType, uint64_t off, + const Atom *t, Reference::Addend a) { + this->addReferenceELF(Reference::KindArch::x86_64, relocType, off, t, a); + } + + void addReferenceELF_Mips(Reference::KindValue relocType, uint64_t off, + const Atom *t, Reference::Addend a) { + this->addReferenceELF(Reference::KindArch::Mips, relocType, off, t, a); + } + + void addReferenceELF_AArch64(Reference::KindValue relocType, uint64_t off, + const Atom *t, Reference::Addend a) { + this->addReferenceELF(Reference::KindArch::AArch64, relocType, off, t, a); + } + + void addReferenceELF_ARM(Reference::KindValue relocType, uint64_t off, + const Atom *t, Reference::Addend a) { + this->addReferenceELF(Reference::KindArch::ARM, relocType, off, t, a); + } +}; + +/// \brief Atom which represents an object for which a COPY relocation will be +/// generated. +class ObjectAtom : public SimpleELFDefinedAtom { +public: + ObjectAtom(const File &f) : SimpleELFDefinedAtom(f) {} + + Scope scope() const override { return scopeGlobal; } + + SectionChoice sectionChoice() const override { return sectionBasedOnContent; } + + ContentType contentType() const override { return typeZeroFill; } + + uint64_t size() const override { return _size; } + + DynamicExport dynamicExport() const override { return dynamicExportAlways; } + + ContentPermissions permissions() const override { return permRW_; } + + ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } + + Alignment alignment() const override { + // The alignment should be 8 byte aligned + return Alignment(3); + } + + StringRef name() const override { return _name; } + + std::string _name; + uint64_t _size; +}; + +class GOTAtom : public SimpleELFDefinedAtom { + StringRef _section; + +public: + GOTAtom(const File &f, StringRef secName) + : SimpleELFDefinedAtom(f), _section(secName) {} + + Scope scope() const override { return scopeTranslationUnit; } + + SectionChoice sectionChoice() const override { return sectionCustomRequired; } + + StringRef customSectionName() const override { return _section; } + + ContentType contentType() const override { return typeGOT; } + + uint64_t size() const override { return rawContent().size(); } + + ContentPermissions permissions() const override { return permRW_; } + + Alignment alignment() const override { + // The alignment should be 8 byte aligned + return Alignment(3); + } + +#ifndef NDEBUG + StringRef name() const override { return _name; } + std::string _name; +#else + StringRef name() const override { return ""; } +#endif +}; + +class PLTAtom : public SimpleELFDefinedAtom { + StringRef _section; + +public: + PLTAtom(const File &f, StringRef secName) + : SimpleELFDefinedAtom(f), _section(secName) {} + + Scope scope() const override { return scopeTranslationUnit; } + + SectionChoice sectionChoice() const override { return sectionCustomRequired; } + + StringRef customSectionName() const override { return _section; } + + ContentType contentType() const override { return typeStub; } + + uint64_t size() const override { return rawContent().size(); } + + ContentPermissions permissions() const override { return permR_X; } + + Alignment alignment() const override { + return Alignment(4); // 16 + } + +#ifndef NDEBUG + StringRef name() const override { return _name; } + std::string _name; +#else + StringRef name() const override { return ""; } +#endif +}; + +class PLT0Atom : public PLTAtom { +public: + PLT0Atom(const File &f) : PLTAtom(f, ".plt") { +#ifndef NDEBUG + _name = ".PLT0"; +#endif + } +}; + +class GLOBAL_OFFSET_TABLEAtom : public SimpleELFDefinedAtom { +public: + GLOBAL_OFFSET_TABLEAtom(const File &f) : SimpleELFDefinedAtom(f) {} + + StringRef name() const override { return "_GLOBAL_OFFSET_TABLE_"; } + + Scope scope() const override { return scopeLinkageUnit; } + + SectionChoice sectionChoice() const override { return sectionCustomRequired; } + + StringRef customSectionName() const override { return ".got.plt"; } + + ContentType contentType() const override { return typeGOT; } + + uint64_t size() const override { return 0; } + + ContentPermissions permissions() const override { return permRW_; } + + Alignment alignment() const override { + // Needs 8 byte alignment + return Alignment(3); + } + + ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } +}; + +class DYNAMICAtom : public SimpleELFDefinedAtom { +public: + DYNAMICAtom(const File &f) : SimpleELFDefinedAtom(f) {} + + StringRef name() const override { return "_DYNAMIC"; } + + Scope scope() const override { return scopeLinkageUnit; } + + Merge merge() const override { return mergeNo; } + + SectionChoice sectionChoice() const override { return sectionCustomRequired; } + + StringRef customSectionName() const override { return ".dynamic"; } + + ContentType contentType() const override { return typeData; } + + uint64_t size() const override { return 0; } + + ContentPermissions permissions() const override { return permRW_; } + + Alignment alignment() const override { return Alignment(0); } + + ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } +}; +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/CMakeLists.txt b/lib/ReaderWriter/ELF/CMakeLists.txt new file mode 100644 index 0000000000000..fd4cb669904d9 --- /dev/null +++ b/lib/ReaderWriter/ELF/CMakeLists.txt @@ -0,0 +1,19 @@ +add_llvm_library(lldELF + ELFLinkingContext.cpp + Reader.cpp + Writer.cpp + LINK_LIBS + lldReaderWriter + lldCore + lldYAML + LLVMSupport + ) + +include_directories(.) + +add_subdirectory(X86) +add_subdirectory(X86_64) +add_subdirectory(Mips) +add_subdirectory(Hexagon) +add_subdirectory(AArch64) +add_subdirectory(ARM) diff --git a/lib/ReaderWriter/ELF/Chunk.h b/lib/ReaderWriter/ELF/Chunk.h new file mode 100644 index 0000000000000..2658d023b3a9b --- /dev/null +++ b/lib/ReaderWriter/ELF/Chunk.h @@ -0,0 +1,102 @@ +//===- lib/ReaderWriter/ELF/Chunks.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_CHUNKS_H +#define LLD_READER_WRITER_ELF_CHUNKS_H + +#include "lld/Core/LLVM.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileOutputBuffer.h" +#include <memory> + +namespace lld { +class ELFLinkingContext; + +namespace elf { +class ELFWriter; + +template <class ELFT> class TargetLayout; + +/// \brief A chunk is a contiguous region of space +template<class ELFT> +class Chunk { +public: + + /// \brief Describes the type of Chunk + enum Kind : uint8_t{ ELFHeader, ///< ELF Header + ProgramHeader, ///< Program Header + SectionHeader, ///< Section header + ELFSegment, ///< Segment + ELFSection, ///< Section + AtomSection, ///< A section containing atoms. + Expression ///< A linker script expression + }; + /// \brief the ContentType of the chunk + enum ContentType : uint8_t{ Unknown, Header, Code, Data, Note, TLS }; + + Chunk(StringRef name, Kind kind, const ELFLinkingContext &context) + : _name(name), _kind(kind), _fsize(0), _msize(0), _alignment(0), _order(0), + _ordinal(1), _start(0), _fileoffset(0), _context(context) {} + virtual ~Chunk() {} + // The name of the chunk + StringRef name() const { return _name; } + // Kind of chunk + Kind kind() const { return _kind; } + virtual uint64_t fileSize() const { return _fsize; } + virtual void setFileSize(uint64_t sz) { _fsize = sz; } + virtual void setAlign(uint64_t align) { _alignment = align; } + virtual uint64_t alignment() const { return _alignment; } + + // The ordinal value of the chunk + uint64_t ordinal() const { return _ordinal;} + void setOrdinal(uint64_t newVal) { _ordinal = newVal;} + // The order in which the chunk would appear in the output file + uint64_t order() const { return _order; } + void setOrder(uint32_t order) { _order = order; } + // Output file offset of the chunk + uint64_t fileOffset() const { return _fileoffset; } + void setFileOffset(uint64_t offset) { _fileoffset = offset; } + // Output start address of the chunk + virtual void setVirtualAddr(uint64_t start) { _start = start; } + virtual uint64_t virtualAddr() const { return _start; } + // Memory size of the chunk + uint64_t memSize() const { return _msize; } + void setMemSize(uint64_t msize) { _msize = msize; } + // Whats the contentType of the chunk? + virtual int getContentType() const = 0; + // Writer the chunk + virtual void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) = 0; + // Finalize the chunk before assigning offsets/virtual addresses + virtual void doPreFlight() = 0; + // Finalize the chunk before writing + virtual void finalize() = 0; + +protected: + StringRef _name; + Kind _kind; + uint64_t _fsize; + uint64_t _msize; + uint64_t _alignment; + uint32_t _order; + uint64_t _ordinal; + uint64_t _start; + uint64_t _fileoffset; + const ELFLinkingContext &_context; +}; + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/CreateELF.h b/lib/ReaderWriter/ELF/CreateELF.h new file mode 100644 index 0000000000000..ad34dddb24d3d --- /dev/null +++ b/lib/ReaderWriter/ELF/CreateELF.h @@ -0,0 +1,118 @@ +//===- lib/ReaderWriter/ELF/CreateELF.h -----------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file provides a simple way to create an object templated on +/// ELFType depending on the runtime type needed. +/// +//===----------------------------------------------------------------------===// +#ifndef LLD_READER_WRITER_ELF_CREATE_ELF_H +#define LLD_READER_WRITER_ELF_CREATE_ELF_H + +#include "llvm/Object/ELF.h" +#include "llvm/Support/Compiler.h" + +namespace { +using llvm::object::ELFType; + +/// \func createELF +/// \brief Create an object depending on the runtime attributes and alignment +/// of an ELF file. +/// +/// \param Traits +/// Traits::result_type must be a type convertable from what create returns. +/// Traits::create must be a template function which takes an ELFType and +/// returns something convertable to Traits::result_type. +/// +/// \param ident pair of EI_CLASS and EI_DATA. +/// \param maxAlignment the maximum alignment of the file. +/// \param args arguments forwarded to CreateELFTraits<T>::create. + +#define LLVM_CREATE_ELF_CreateELFTraits(endian, align, is64, ...) \ + Traits::template create<ELFType<llvm::support::endian, align, is64>>( \ + __VA_ARGS__); + +#if !LLVM_IS_UNALIGNED_ACCESS_FAST +# define LLVM_CREATE_ELF_MaxAlignCheck(normal, low, endian, is64, ...) \ + if (maxAlignment >= normal) \ + return LLVM_CREATE_ELF_CreateELFTraits(endian, normal, is64, __VA_ARGS__) \ + else if (maxAlignment >= low) \ + return LLVM_CREATE_ELF_CreateELFTraits(endian, low, is64, __VA_ARGS__) \ + else \ + llvm_unreachable("Invalid alignment for ELF file!"); +#else +# define LLVM_CREATE_ELF_MaxAlignCheck(normal, low, endian, is64, ...) \ + if (maxAlignment >= low) \ + return LLVM_CREATE_ELF_CreateELFTraits(endian, low, is64, __VA_ARGS__) \ + else \ + llvm_unreachable("Invalid alignment for ELF file!"); +#endif + +#define LLVM_CREATE_ELF_IMPL(...) \ + if (ident.first == llvm::ELF::ELFCLASS32 && \ + ident.second == llvm::ELF::ELFDATA2LSB) { \ + LLVM_CREATE_ELF_MaxAlignCheck(4, 2, little, false, __VA_ARGS__) \ + } else if (ident.first == llvm::ELF::ELFCLASS32 && \ + ident.second == llvm::ELF::ELFDATA2MSB) { \ + LLVM_CREATE_ELF_MaxAlignCheck(4, 2, big, false, __VA_ARGS__) \ + } else if (ident.first == llvm::ELF::ELFCLASS64 && \ + ident.second == llvm::ELF::ELFDATA2MSB) { \ + LLVM_CREATE_ELF_MaxAlignCheck(8, 2, big, true, __VA_ARGS__) \ + } else if (ident.first == llvm::ELF::ELFCLASS64 && \ + ident.second == llvm::ELF::ELFDATA2LSB) { \ + LLVM_CREATE_ELF_MaxAlignCheck(8, 2, little, true, __VA_ARGS__) \ + } \ + llvm_unreachable("Invalid ELF type!"); + +#if LLVM_HAS_VARIADIC_TEMPLATES +template <class Traits, class ...Args> +typename Traits::result_type createELF( + std::pair<unsigned char, unsigned char> ident, std::size_t maxAlignment, + Args &&...args) { + LLVM_CREATE_ELF_IMPL(std::forward<Args>(args)...) +} +#else +template <class Traits, class T1> +typename Traits::result_type createELF( + std::pair<unsigned char, unsigned char> ident, std::size_t maxAlignment, + T1 &&t1) { + LLVM_CREATE_ELF_IMPL(std::forward<T1>(t1)) +} + +template <class Traits, class T1, class T2> +typename Traits::result_type createELF( + std::pair<unsigned char, unsigned char> ident, std::size_t maxAlignment, + T1 &&t1, T2 &&t2) { + LLVM_CREATE_ELF_IMPL(std::forward<T1>(t1), std::forward<T2>(t2)) +} + +template <class Traits, class T1, class T2, class T3> +typename Traits::result_type createELF( + std::pair<unsigned char, unsigned char> ident, std::size_t maxAlignment, + T1 &&t1, T2 &&t2, T3 &&t3) { + LLVM_CREATE_ELF_IMPL(std::forward<T1>(t1), std::forward<T2>(t2), + std::forward<T3>(t3)) +} + +template <class Traits, class T1, class T2, class T3, class T4> +typename Traits::result_type createELF( + std::pair<unsigned char, unsigned char> ident, std::size_t maxAlignment, + T1 &&t1, T2 &&t2, T3 &&t3, T4 &&t4) { + LLVM_CREATE_ELF_IMPL(std::forward<T1>(t1), std::forward<T2>(t2), + std::forward<T3>(t3), std::forward<T4>(t4)) +} + +#endif // LLVM_HAS_VARIADIC_TEMPLATES +} // end anon namespace + +#undef LLVM_CREATE_ELF_CreateELFTraits +#undef LLVM_CREATE_ELF_MaxAlignCheck +#undef LLVM_CREATE_ELF_IMPL + +#endif diff --git a/lib/ReaderWriter/ELF/DefaultLayout.h b/lib/ReaderWriter/ELF/DefaultLayout.h new file mode 100644 index 0000000000000..9af3b8eb8dc63 --- /dev/null +++ b/lib/ReaderWriter/ELF/DefaultLayout.h @@ -0,0 +1,1050 @@ +//===- lib/ReaderWriter/ELF/DefaultLayout.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_DEFAULT_LAYOUT_H +#define LLD_READER_WRITER_ELF_DEFAULT_LAYOUT_H + +#include "Atoms.h" +#include "Chunk.h" +#include "HeaderChunks.h" +#include "Layout.h" +#include "SectionChunks.h" +#include "SegmentChunks.h" +#include "lld/Core/Instrumentation.h" +#include "lld/Core/STDExtras.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Format.h" +#include <map> +#include <unordered_map> + +namespace lld { +namespace elf { +/// \brief The DefaultLayout class is used by the Writer to arrange +/// sections and segments in the order determined by the target ELF +/// format. The writer creates a single instance of the DefaultLayout +/// class +template<class ELFT> +class DefaultLayout : public Layout { +public: + + // The order in which the sections appear in the output file + // If its determined, that the layout needs to change + // just changing the order of enumerations would essentially + // change the layout in the output file + // Change the enumerations so that Target can override and stick + // a section anywhere it wants to + enum DefaultSectionOrder { + ORDER_NOT_DEFINED = 0, + ORDER_INTERP = 10, + ORDER_RO_NOTE = 15, + ORDER_HASH = 30, + ORDER_DYNAMIC_SYMBOLS = 40, + ORDER_DYNAMIC_STRINGS = 50, + ORDER_DYNAMIC_RELOCS = 52, + ORDER_DYNAMIC_PLT_RELOCS = 54, + ORDER_INIT = 60, + ORDER_PLT = 70, + ORDER_TEXT = 80, + ORDER_FINI = 90, + ORDER_REL = 95, + ORDER_RODATA = 100, + ORDER_EH_FRAME = 110, + ORDER_EH_FRAMEHDR = 120, + ORDER_TDATA = 124, + ORDER_TBSS = 128, + ORDER_CTORS = 130, + ORDER_DTORS = 140, + ORDER_INIT_ARRAY = 150, + ORDER_FINI_ARRAY = 160, + ORDER_DYNAMIC = 170, + ORDER_GOT = 180, + ORDER_GOT_PLT = 190, + ORDER_DATA = 200, + ORDER_RW_NOTE = 205, + ORDER_BSS = 210, + ORDER_NOALLOC = 215, + ORDER_OTHER = 220, + ORDER_SECTION_STRINGS = 230, + ORDER_SYMBOL_TABLE = 240, + ORDER_STRING_TABLE = 250, + ORDER_SECTION_HEADERS = 260 + }; + +public: + + // The Key used for creating Sections + // The sections are created using + // SectionName, contentPermissions + struct SectionKey { + SectionKey(StringRef name, DefinedAtom::ContentPermissions perm, + StringRef path) + : _name(name), _perm(perm), _path(path) {} + + // Data members + StringRef _name; + DefinedAtom::ContentPermissions _perm; + StringRef _path; + }; + + struct SectionKeyHash { + int64_t operator()(const SectionKey &k) const { + return llvm::hash_combine(k._name, k._perm, k._path); + } + }; + + struct SectionKeyEq { + bool operator()(const SectionKey &lhs, const SectionKey &rhs) const { + return ((lhs._name == rhs._name) && (lhs._perm == rhs._perm) && + (lhs._path == rhs._path)); + } + }; + + typedef typename std::vector<Chunk<ELFT> *>::iterator ChunkIter; + typedef typename std::vector<Segment<ELFT> *>::iterator SegmentIter; + + // The additional segments are used to figure out + // if there is a segment by that type already created + // For example : PT_TLS, we have two sections .tdata/.tbss + // that are part of PT_TLS, we need to create this additional + // segment only once + typedef std::pair<int64_t, int64_t> AdditionalSegmentKey; + // The segments are created using + // SegmentName, Segment flags + typedef std::pair<StringRef, int64_t> SegmentKey; + + // HashKey for the Segment + class SegmentHashKey { + public: + int64_t operator() (const SegmentKey &k) const { + // k.first = SegmentName + // k.second = SegmentFlags + return llvm::hash_combine(k.first, k.second); + } + }; + + class AdditionalSegmentHashKey { + public: + int64_t operator()(const AdditionalSegmentKey &k) const { + // k.first = SegmentName + // k.second = SegmentFlags + return llvm::hash_combine(k.first, k.second); + } + }; + + // Output Sections contain the map of Sectionnames to a vector of sections, + // that have been merged to form a single section + typedef llvm::StringMap<OutputSection<ELFT> *> OutputSectionMapT; + typedef + typename std::vector<OutputSection<ELFT> *>::iterator OutputSectionIter; + + typedef std::unordered_map<SectionKey, AtomSection<ELFT> *, SectionKeyHash, + SectionKeyEq> SectionMapT; + typedef std::unordered_map<AdditionalSegmentKey, Segment<ELFT> *, + AdditionalSegmentHashKey> AdditionalSegmentMapT; + typedef std::unordered_map<SegmentKey, Segment<ELFT> *, SegmentHashKey> + SegmentMapT; + + /// \brief find a absolute atom pair given a absolute atom name + struct FindByName { + const std::string _name; + FindByName(StringRef name) : _name(name) {} + bool operator()(const lld::AtomLayout *j) { return j->_atom->name() == _name; } + }; + + typedef typename std::vector<lld::AtomLayout *>::iterator AbsoluteAtomIterT; + + typedef llvm::DenseSet<const Atom *> AtomSetT; + + DefaultLayout(ELFLinkingContext &context) + : _context(context), _linkerScriptSema(context.linkerScriptSema()) {} + + /// \brief Return the section order for a input section + SectionOrder getSectionOrder(StringRef name, int32_t contentType, + int32_t contentPermissions) override; + + /// \brief Return the name of the input section by decoding the input + /// sectionChoice. + virtual StringRef getInputSectionName(const DefinedAtom *da) const; + + /// \brief Return the name of the output section from the input section. + virtual StringRef getOutputSectionName(StringRef archivePath, + StringRef memberPath, + StringRef inputSectionName) const; + + /// \brief Gets or creates a section. + AtomSection<ELFT> * + getSection(StringRef name, int32_t contentType, + DefinedAtom::ContentPermissions contentPermissions, + const DefinedAtom *da); + + /// \brief Gets the segment for a output section + virtual Layout::SegmentType getSegmentType(Section<ELFT> *section) const; + + /// \brief Returns true/false depending on whether the section has a Output + // segment or not + static bool hasOutputSegment(Section<ELFT> *section); + + // Adds an atom to the section + ErrorOr<const lld::AtomLayout *> addAtom(const Atom *atom) override; + + /// \brief Find an output Section given a section name. + OutputSection<ELFT> *findOutputSection(StringRef name) { + auto iter = _outputSectionMap.find(name); + if (iter == _outputSectionMap.end()) + return nullptr; + return iter->second; + } + + /// \brief find a absolute atom given a name + AbsoluteAtomIterT findAbsoluteAtom(StringRef name) { + return std::find_if(_absoluteAtoms.begin(), _absoluteAtoms.end(), + FindByName(name)); + } + + // Output sections with the same name into a OutputSection + void createOutputSections(); + + /// \brief Sort the sections by their order as defined by the layout, + /// preparing all sections to be assigned to a segment. + virtual void sortInputSections(); + + /// \brief Add extra chunks to a segment just before including the input + /// section given by <archivePath, memberPath, sectionName>. This + /// is used to add linker script expressions before each section. + virtual void addExtraChunksToSegment(Segment<ELFT> *segment, + StringRef archivePath, + StringRef memberPath, + StringRef sectionName); + + void assignSectionsToSegments() override; + + void assignVirtualAddress() override; + + void assignFileOffsetsForMiscSections(); + + range<AbsoluteAtomIterT> absoluteAtoms() { return _absoluteAtoms; } + + void addSection(Chunk<ELFT> *c) { _sections.push_back(c); } + + void finalize() { + ScopedTask task(getDefaultDomain(), "Finalize layout"); + for (auto &si : _sections) + si->finalize(); + } + + void doPreFlight() { + for (auto &si : _sections) + si->doPreFlight(); + } + + const AtomLayout *findAtomLayoutByName(StringRef name) const override { + for (auto sec : _sections) + if (auto section = dyn_cast<Section<ELFT>>(sec)) + if (auto *al = section->findAtomLayoutByName(name)) + return al; + return nullptr; + } + + void setHeader(ELFHeader<ELFT> *elfHeader) { _elfHeader = elfHeader; } + + void setProgramHeader(ProgramHeader<ELFT> *p) { + _programHeader = p; + } + + range<OutputSectionIter> outputSections() { return _outputSections; } + + range<ChunkIter> sections() { return _sections; } + + range<SegmentIter> segments() { return _segments; } + + ELFHeader<ELFT> *getHeader() { return _elfHeader; } + + bool hasDynamicRelocationTable() const { return !!_dynamicRelocationTable; } + + bool hasPLTRelocationTable() const { return !!_pltRelocationTable; } + + /// \brief Get or create the dynamic relocation table. All relocations in this + /// table are processed at startup. + RelocationTable<ELFT> *getDynamicRelocationTable() { + if (!_dynamicRelocationTable) { + _dynamicRelocationTable = std::move(createRelocationTable( + _context.isRelaOutputFormat() ? ".rela.dyn" : ".rel.dyn", + ORDER_DYNAMIC_RELOCS)); + addSection(_dynamicRelocationTable.get()); + } + return _dynamicRelocationTable.get(); + } + + /// \brief Get or create the PLT relocation table. Referenced by DT_JMPREL. + RelocationTable<ELFT> *getPLTRelocationTable() { + if (!_pltRelocationTable) { + _pltRelocationTable = std::move(createRelocationTable( + _context.isRelaOutputFormat() ? ".rela.plt" : ".rel.plt", + ORDER_DYNAMIC_PLT_RELOCS)); + addSection(_pltRelocationTable.get()); + } + return _pltRelocationTable.get(); + } + + uint64_t getTLSSize() const { + for (const auto &phdr : *_programHeader) + if (phdr->p_type == llvm::ELF::PT_TLS) + return phdr->p_memsz; + return 0; + } + + bool isReferencedByDefinedAtom(const Atom *a) const { + return _referencedDynAtoms.count(a); + } + + bool isCopied(const SharedLibraryAtom *sla) const { + return _copiedDynSymNames.count(sla->name()); + } + + /// \brief Handle SORT_BY_PRIORITY. + void sortOutputSectionByPriority(StringRef outputSectionName, + StringRef prefix); + +protected: + /// \brief TargetLayouts may use these functions to reorder the input sections + /// in a order defined by their ABI. + virtual void finalizeOutputSectionLayout() {} + + /// \brief Allocate a new section. + virtual AtomSection<ELFT> *createSection( + StringRef name, int32_t contentType, + DefinedAtom::ContentPermissions contentPermissions, + SectionOrder sectionOrder); + + /// \brief Create a new relocation table. + virtual unique_bump_ptr<RelocationTable<ELFT>> + createRelocationTable(StringRef name, int32_t order) { + return unique_bump_ptr<RelocationTable<ELFT>>( + new (_allocator) RelocationTable<ELFT>(_context, name, order)); + } + +private: + /// Helper function that returns the priority value from an input section. + uint32_t getPriorityFromSectionName(StringRef sectionName) const; + +protected: + llvm::BumpPtrAllocator _allocator; + SectionMapT _sectionMap; + OutputSectionMapT _outputSectionMap; + AdditionalSegmentMapT _additionalSegmentMap; + SegmentMapT _segmentMap; + std::vector<Chunk<ELFT> *> _sections; + std::vector<Segment<ELFT> *> _segments; + std::vector<OutputSection<ELFT> *> _outputSections; + ELFHeader<ELFT> *_elfHeader; + ProgramHeader<ELFT> *_programHeader; + unique_bump_ptr<RelocationTable<ELFT>> _dynamicRelocationTable; + unique_bump_ptr<RelocationTable<ELFT>> _pltRelocationTable; + std::vector<lld::AtomLayout *> _absoluteAtoms; + AtomSetT _referencedDynAtoms; + llvm::StringSet<> _copiedDynSymNames; + ELFLinkingContext &_context; + script::Sema &_linkerScriptSema; +}; + +template <class ELFT> +Layout::SectionOrder DefaultLayout<ELFT>::getSectionOrder( + StringRef name, int32_t contentType, int32_t contentPermissions) { + switch (contentType) { + case DefinedAtom::typeResolver: + case DefinedAtom::typeCode: + return llvm::StringSwitch<Layout::SectionOrder>(name) + .StartsWith(".eh_frame_hdr", ORDER_EH_FRAMEHDR) + .StartsWith(".eh_frame", ORDER_EH_FRAME) + .StartsWith(".init", ORDER_INIT) + .StartsWith(".fini", ORDER_FINI) + .StartsWith(".hash", ORDER_HASH) + .Default(ORDER_TEXT); + + case DefinedAtom::typeConstant: + return ORDER_RODATA; + + case DefinedAtom::typeData: + case DefinedAtom::typeDataFast: + return llvm::StringSwitch<Layout::SectionOrder>(name) + .StartsWith(".init_array", ORDER_INIT_ARRAY) + .StartsWith(".fini_array", ORDER_FINI_ARRAY) + .StartsWith(".dynamic", ORDER_DYNAMIC) + .StartsWith(".ctors", ORDER_CTORS) + .StartsWith(".dtors", ORDER_DTORS) + .Default(ORDER_DATA); + + case DefinedAtom::typeZeroFill: + case DefinedAtom::typeZeroFillFast: + return ORDER_BSS; + + case DefinedAtom::typeGOT: + return llvm::StringSwitch<Layout::SectionOrder>(name) + .StartsWith(".got.plt", ORDER_GOT_PLT) + .Default(ORDER_GOT); + + case DefinedAtom::typeStub: + return ORDER_PLT; + + case DefinedAtom::typeRONote: + return ORDER_RO_NOTE; + + case DefinedAtom::typeRWNote: + return ORDER_RW_NOTE; + + case DefinedAtom::typeNoAlloc: + return ORDER_NOALLOC; + + case DefinedAtom::typeThreadData: + return ORDER_TDATA; + case DefinedAtom::typeThreadZeroFill: + return ORDER_TBSS; + default: + // If we get passed in a section push it to OTHER + if (contentPermissions == DefinedAtom::perm___) + return ORDER_OTHER; + + return ORDER_NOT_DEFINED; + } +} + +/// \brief This maps the input sections to the output section names +template <class ELFT> +StringRef +DefaultLayout<ELFT>::getInputSectionName(const DefinedAtom *da) const { + if (da->sectionChoice() == DefinedAtom::sectionBasedOnContent) { + switch (da->contentType()) { + case DefinedAtom::typeCode: + return ".text"; + case DefinedAtom::typeData: + return ".data"; + case DefinedAtom::typeConstant: + return ".rodata"; + case DefinedAtom::typeZeroFill: + return ".bss"; + case DefinedAtom::typeThreadData: + return ".tdata"; + case DefinedAtom::typeThreadZeroFill: + return ".tbss"; + default: + break; + } + } + return da->customSectionName(); +} + +/// \brief This maps the input sections to the output section names. +template <class ELFT> +StringRef +DefaultLayout<ELFT>::getOutputSectionName(StringRef archivePath, + StringRef memberPath, + StringRef inputSectionName) const { + StringRef outputSectionName; + if (_linkerScriptSema.hasLayoutCommands()) { + script::Sema::SectionKey key = {archivePath, memberPath, inputSectionName}; + outputSectionName = _linkerScriptSema.getOutputSection(key); + if (!outputSectionName.empty()) + return outputSectionName; + } + return llvm::StringSwitch<StringRef>(inputSectionName) + .StartsWith(".text", ".text") + .StartsWith(".ctors", ".ctors") + .StartsWith(".dtors", ".dtors") + .StartsWith(".rodata", ".rodata") + .StartsWith(".gcc_except_table", ".gcc_except_table") + .StartsWith(".data.rel.ro", ".data.rel.ro") + .StartsWith(".data.rel.local", ".data.rel.local") + .StartsWith(".data", ".data") + .StartsWith(".tdata", ".tdata") + .StartsWith(".tbss", ".tbss") + .StartsWith(".init_array", ".init_array") + .StartsWith(".fini_array", ".fini_array") + .Default(inputSectionName); +} + +/// \brief Gets the segment for a output section +template <class ELFT> +Layout::SegmentType DefaultLayout<ELFT>::getSegmentType( + Section<ELFT> *section) const { + + switch (section->order()) { + case ORDER_INTERP: + return llvm::ELF::PT_INTERP; + + case ORDER_TEXT: + case ORDER_HASH: + case ORDER_DYNAMIC_SYMBOLS: + case ORDER_DYNAMIC_STRINGS: + case ORDER_DYNAMIC_RELOCS: + case ORDER_DYNAMIC_PLT_RELOCS: + case ORDER_REL: + case ORDER_INIT: + case ORDER_PLT: + case ORDER_FINI: + case ORDER_RODATA: + case ORDER_EH_FRAME: + case ORDER_CTORS: + case ORDER_DTORS: + return llvm::ELF::PT_LOAD; + + case ORDER_RO_NOTE: + case ORDER_RW_NOTE: + return llvm::ELF::PT_NOTE; + + case ORDER_DYNAMIC: + return llvm::ELF::PT_DYNAMIC; + + case ORDER_EH_FRAMEHDR: + return llvm::ELF::PT_GNU_EH_FRAME; + + case ORDER_GOT: + case ORDER_GOT_PLT: + case ORDER_DATA: + case ORDER_BSS: + case ORDER_INIT_ARRAY: + case ORDER_FINI_ARRAY: + return llvm::ELF::PT_LOAD; + + case ORDER_TDATA: + case ORDER_TBSS: + return llvm::ELF::PT_TLS; + + default: + return llvm::ELF::PT_NULL; + } +} + +template <class ELFT> +bool DefaultLayout<ELFT>::hasOutputSegment(Section<ELFT> *section) { + switch (section->order()) { + case ORDER_INTERP: + case ORDER_HASH: + case ORDER_DYNAMIC_SYMBOLS: + case ORDER_DYNAMIC_STRINGS: + case ORDER_DYNAMIC_RELOCS: + case ORDER_DYNAMIC_PLT_RELOCS: + case ORDER_REL: + case ORDER_INIT: + case ORDER_PLT: + case ORDER_TEXT: + case ORDER_FINI: + case ORDER_RODATA: + case ORDER_EH_FRAME: + case ORDER_EH_FRAMEHDR: + case ORDER_TDATA: + case ORDER_TBSS: + case ORDER_RO_NOTE: + case ORDER_RW_NOTE: + case ORDER_DYNAMIC: + case ORDER_CTORS: + case ORDER_DTORS: + case ORDER_GOT: + case ORDER_GOT_PLT: + case ORDER_DATA: + case ORDER_INIT_ARRAY: + case ORDER_FINI_ARRAY: + case ORDER_BSS: + case ORDER_NOALLOC: + return true; + default: + return section->hasOutputSegment(); + } +} + +template <class ELFT> +AtomSection<ELFT> *DefaultLayout<ELFT>::createSection( + StringRef sectionName, int32_t contentType, + DefinedAtom::ContentPermissions permissions, SectionOrder sectionOrder) { + return new (_allocator) AtomSection<ELFT>(_context, sectionName, contentType, + permissions, sectionOrder); +} + +template <class ELFT> +AtomSection<ELFT> * +DefaultLayout<ELFT>::getSection(StringRef sectionName, int32_t contentType, + DefinedAtom::ContentPermissions permissions, + const DefinedAtom *da) { + const SectionKey sectionKey(sectionName, permissions, da->file().path()); + SectionOrder sectionOrder = getSectionOrder(sectionName, contentType, permissions); + auto sec = _sectionMap.find(sectionKey); + if (sec != _sectionMap.end()) + return sec->second; + AtomSection<ELFT> *newSec = + createSection(sectionName, contentType, permissions, sectionOrder); + + newSec->setOutputSectionName(getOutputSectionName( + da->file().archivePath(), da->file().memberPath(), sectionName)); + newSec->setOrder(sectionOrder); + newSec->setArchiveNameOrPath(da->file().archivePath()); + newSec->setMemberNameOrPath(da->file().memberPath()); + _sections.push_back(newSec); + _sectionMap.insert(std::make_pair(sectionKey, newSec)); + return newSec; +} + +template <class ELFT> +ErrorOr<const lld::AtomLayout *> +DefaultLayout<ELFT>::addAtom(const Atom *atom) { + if (const DefinedAtom *definedAtom = dyn_cast<DefinedAtom>(atom)) { + // HACK: Ignore undefined atoms. We need to adjust the interface so that + // undefined atoms can still be included in the output symbol table for + // -noinhibit-exec. + if (definedAtom->contentType() == DefinedAtom::typeUnknown) + return make_error_code(llvm::errc::invalid_argument); + const DefinedAtom::ContentPermissions permissions = + definedAtom->permissions(); + const DefinedAtom::ContentType contentType = definedAtom->contentType(); + + StringRef sectionName = getInputSectionName(definedAtom); + AtomSection<ELFT> *section = + getSection(sectionName, contentType, permissions, definedAtom); + + // Add runtime relocations to the .rela section. + for (const auto &reloc : *definedAtom) { + bool isLocalReloc = true; + if (_context.isDynamicRelocation(*reloc)) { + getDynamicRelocationTable()->addRelocation(*definedAtom, *reloc); + isLocalReloc = false; + } else if (_context.isPLTRelocation(*reloc)) { + getPLTRelocationTable()->addRelocation(*definedAtom, *reloc); + isLocalReloc = false; + } + + if (!reloc->target()) + continue; + + //Ignore undefined atoms that are not target of dynamic relocations + if (isa<UndefinedAtom>(reloc->target()) && isLocalReloc) + continue; + + if (_context.isCopyRelocation(*reloc)) { + _copiedDynSymNames.insert(definedAtom->name()); + continue; + } + + _referencedDynAtoms.insert(reloc->target()); + } + + return section->appendAtom(atom); + } else if (const AbsoluteAtom *absoluteAtom = dyn_cast<AbsoluteAtom>(atom)) { + // Absolute atoms are not part of any section, they are global for the whole + // link + _absoluteAtoms.push_back(new (_allocator) + lld::AtomLayout(absoluteAtom, 0, absoluteAtom->value())); + return _absoluteAtoms.back(); + } else { + llvm_unreachable("Only absolute / defined atoms can be added here"); + } +} + +/// Output sections with the same name into a OutputSection +template <class ELFT> void DefaultLayout<ELFT>::createOutputSections() { + OutputSection<ELFT> *outputSection; + + for (auto &si : _sections) { + Section<ELFT> *section = dyn_cast<Section<ELFT>>(si); + if (!section) + continue; + const std::pair<StringRef, OutputSection<ELFT> *> currentOutputSection( + section->outputSectionName(), nullptr); + std::pair<typename OutputSectionMapT::iterator, bool> outputSectionInsert( + _outputSectionMap.insert(currentOutputSection)); + if (!outputSectionInsert.second) { + outputSection = outputSectionInsert.first->second; + } else { + outputSection = new (_allocator.Allocate<OutputSection<ELFT>>()) + OutputSection<ELFT>(section->outputSectionName()); + _outputSections.push_back(outputSection); + outputSectionInsert.first->second = outputSection; + } + outputSection->appendSection(si); + } +} + +template <class ELFT> +uint32_t +DefaultLayout<ELFT>::getPriorityFromSectionName(StringRef sectionName) const { + StringRef priority = sectionName.drop_front().rsplit('.').second; + uint32_t prio; + if (priority.getAsInteger(10, prio)) + return std::numeric_limits<uint32_t>::max(); + return prio; +} + +template <class ELFT> +void DefaultLayout<ELFT>::sortOutputSectionByPriority( + StringRef outputSectionName, StringRef prefix) { + OutputSection<ELFT> *outputSection = findOutputSection(outputSectionName); + if (!outputSection) + return; + + auto sections = outputSection->sections(); + + std::sort(sections.begin(), sections.end(), + [&](Chunk<ELFT> *lhs, Chunk<ELFT> *rhs) { + Section<ELFT> *lhsSection = dyn_cast<Section<ELFT>>(lhs); + Section<ELFT> *rhsSection = dyn_cast<Section<ELFT>>(rhs); + if (!lhsSection || !rhsSection) + return false; + StringRef lhsSectionName = lhsSection->inputSectionName(); + StringRef rhsSectionName = rhsSection->inputSectionName(); + + if (!prefix.empty()) { + if (!lhsSectionName.startswith(prefix) || + !rhsSectionName.startswith(prefix)) + return false; + } + return getPriorityFromSectionName(lhsSectionName) < + getPriorityFromSectionName(rhsSectionName); + }); +} + +template <class ELFT> void DefaultLayout<ELFT>::assignSectionsToSegments() { + ScopedTask task(getDefaultDomain(), "assignSectionsToSegments"); + ELFLinkingContext::OutputMagic outputMagic = _context.getOutputMagic(); + // sort the sections by their order as defined by the layout + sortInputSections(); + + // Create output sections. + createOutputSections(); + + // Finalize output section layout. + finalizeOutputSectionLayout(); + + // Set the ordinal after sorting the sections + int ordinal = 1; + for (auto osi : _outputSections) { + osi->setOrdinal(ordinal); + for (auto ai : osi->sections()) { + ai->setOrdinal(ordinal); + } + ++ordinal; + } + for (auto osi : _outputSections) { + for (auto ai : osi->sections()) { + if (auto section = dyn_cast<Section<ELFT> >(ai)) { + if (!hasOutputSegment(section)) + continue; + + osi->setLoadableSection(section->isLoadableSection()); + + // Get the segment type for the section + int64_t segmentType = getSegmentType(section); + + osi->setHasSegment(); + section->setSegmentType(segmentType); + StringRef segmentName = section->segmentKindToStr(); + + int64_t lookupSectionFlag = osi->flags(); + if ((!(lookupSectionFlag & llvm::ELF::SHF_WRITE)) && + (_context.mergeRODataToTextSegment())) + lookupSectionFlag &= ~llvm::ELF::SHF_EXECINSTR; + + // Merge string sections into Data segment itself + lookupSectionFlag &= ~(llvm::ELF::SHF_STRINGS | llvm::ELF::SHF_MERGE); + + // Merge the TLS section into the DATA segment itself + lookupSectionFlag &= ~(llvm::ELF::SHF_TLS); + + Segment<ELFT> *segment; + // We need a separate segment for sections that don't have + // the segment type to be PT_LOAD + if (segmentType != llvm::ELF::PT_LOAD) { + const AdditionalSegmentKey key(segmentType, lookupSectionFlag); + const std::pair<AdditionalSegmentKey, Segment<ELFT> *> + additionalSegment(key, nullptr); + std::pair<typename AdditionalSegmentMapT::iterator, bool> + additionalSegmentInsert( + _additionalSegmentMap.insert(additionalSegment)); + if (!additionalSegmentInsert.second) { + segment = additionalSegmentInsert.first->second; + } else { + segment = new (_allocator) + Segment<ELFT>(_context, segmentName, segmentType); + additionalSegmentInsert.first->second = segment; + _segments.push_back(segment); + } + segment->append(section); + } + if (segmentType == llvm::ELF::PT_NULL) + continue; + + // If the output magic is set to OutputMagic::NMAGIC or + // OutputMagic::OMAGIC, Place the data alongside text in one single + // segment + if (outputMagic == ELFLinkingContext::OutputMagic::NMAGIC || + outputMagic == ELFLinkingContext::OutputMagic::OMAGIC) + lookupSectionFlag = llvm::ELF::SHF_EXECINSTR | llvm::ELF::SHF_ALLOC | + llvm::ELF::SHF_WRITE; + + // Use the flags of the merged Section for the segment + const SegmentKey key("PT_LOAD", lookupSectionFlag); + const std::pair<SegmentKey, Segment<ELFT> *> currentSegment(key, + nullptr); + std::pair<typename SegmentMapT::iterator, bool> segmentInsert( + _segmentMap.insert(currentSegment)); + if (!segmentInsert.second) { + segment = segmentInsert.first->second; + } else { + segment = new (_allocator) + Segment<ELFT>(_context, "PT_LOAD", llvm::ELF::PT_LOAD); + segmentInsert.first->second = segment; + _segments.push_back(segment); + } + // Insert chunks with linker script expressions that occur at this + // point, just before appending a new input section + addExtraChunksToSegment(segment, section->archivePath(), + section->memberPath(), + section->inputSectionName()); + segment->append(section); + } + } + } + if (_context.isDynamic() && !_context.isDynamicLibrary()) { + Segment<ELFT> *segment = + new (_allocator) ProgramHeaderSegment<ELFT>(_context); + _segments.push_back(segment); + segment->append(_elfHeader); + segment->append(_programHeader); + } +} + +template<class ELFT> +void +DefaultLayout<ELFT>::assignVirtualAddress() { + if (_segments.empty()) + return; + + std::sort(_segments.begin(), _segments.end(), Segment<ELFT>::compareSegments); + + uint64_t baseAddress = _context.getBaseAddress(); + + // HACK: This is a super dirty hack. The elf header and program header are + // not part of a section, but we need them to be loaded at the base address + // so that AT_PHDR is set correctly by the loader and so they are accessible + // at runtime. To do this we simply prepend them to the first loadable Segment + // and let the layout logic take care of it. + Segment<ELFT> *firstLoadSegment = nullptr; + for (auto si : _segments) { + if (si->segmentType() == llvm::ELF::PT_LOAD) { + firstLoadSegment = si; + si->firstSection()->setAlign(si->alignment()); + break; + } + } + assert(firstLoadSegment != nullptr && "No loadable segment!"); + firstLoadSegment->prepend(_programHeader); + firstLoadSegment->prepend(_elfHeader); + bool newSegmentHeaderAdded = true; + bool virtualAddressAssigned = false; + bool fileOffsetAssigned = false; + while (true) { + for (auto si : _segments) { + si->finalize(); + // Don't add PT_NULL segments into the program header + if (si->segmentType() != llvm::ELF::PT_NULL) + newSegmentHeaderAdded = _programHeader->addSegment(si); + } + if (!newSegmentHeaderAdded && virtualAddressAssigned) + break; + uint64_t address = baseAddress; + // start assigning virtual addresses + for (auto &si : _segments) { + if ((si->segmentType() != llvm::ELF::PT_LOAD) && + (si->segmentType() != llvm::ELF::PT_NULL)) + continue; + + if (si->segmentType() == llvm::ELF::PT_NULL) { + si->assignVirtualAddress(0 /*non loadable*/); + } else { + if (virtualAddressAssigned && (address != baseAddress) && + (address == si->virtualAddr())) + break; + si->assignVirtualAddress(address); + } + address = si->virtualAddr() + si->memSize(); + } + uint64_t baseFileOffset = 0; + uint64_t fileoffset = baseFileOffset; + for (auto &si : _segments) { + if ((si->segmentType() != llvm::ELF::PT_LOAD) && + (si->segmentType() != llvm::ELF::PT_NULL)) + continue; + if (fileOffsetAssigned && (fileoffset != baseFileOffset) && + (fileoffset == si->fileOffset())) + break; + si->assignFileOffsets(fileoffset); + fileoffset = si->fileOffset() + si->fileSize(); + } + virtualAddressAssigned = true; + fileOffsetAssigned = true; + _programHeader->resetProgramHeaders(); + } + Section<ELFT> *section; + // Fix the offsets of all the atoms within a section + for (auto &si : _sections) { + section = dyn_cast<Section<ELFT>>(si); + if (section && DefaultLayout<ELFT>::hasOutputSegment(section)) + section->assignFileOffsets(section->fileOffset()); + } + // Set the size of the merged Sections + for (auto osi : _outputSections) { + uint64_t sectionfileoffset = 0; + uint64_t startFileOffset = 0; + uint64_t sectionsize = 0; + bool isFirstSection = true; + for (auto si : osi->sections()) { + if (isFirstSection) { + startFileOffset = si->fileOffset(); + isFirstSection = false; + } + sectionfileoffset = si->fileOffset(); + sectionsize = si->fileSize(); + } + sectionsize = (sectionfileoffset - startFileOffset) + sectionsize; + osi->setFileOffset(startFileOffset); + osi->setSize(sectionsize); + } + // Set the virtual addr of the merged Sections + for (auto osi : _outputSections) { + uint64_t sectionstartaddr = 0; + uint64_t startaddr = 0; + uint64_t sectionsize = 0; + bool isFirstSection = true; + for (auto si : osi->sections()) { + if (isFirstSection) { + startaddr = si->virtualAddr(); + isFirstSection = false; + } + sectionstartaddr = si->virtualAddr(); + sectionsize = si->memSize(); + } + sectionsize = (sectionstartaddr - startaddr) + sectionsize; + osi->setMemSize(sectionsize); + osi->setAddr(startaddr); + } +} + +template <class ELFT> +void DefaultLayout<ELFT>::assignFileOffsetsForMiscSections() { + uint64_t fileoffset = 0; + uint64_t size = 0; + for (auto si : _segments) { + // Don't calculate offsets from non loadable segments + if ((si->segmentType() != llvm::ELF::PT_LOAD) && + (si->segmentType() != llvm::ELF::PT_NULL)) + continue; + fileoffset = si->fileOffset(); + size = si->fileSize(); + } + fileoffset = fileoffset + size; + Section<ELFT> *section; + for (auto si : _sections) { + section = dyn_cast<Section<ELFT>>(si); + if (section && DefaultLayout<ELFT>::hasOutputSegment(section)) + continue; + fileoffset = llvm::RoundUpToAlignment(fileoffset, si->alignment()); + si->setFileOffset(fileoffset); + si->setVirtualAddr(0); + fileoffset += si->fileSize(); + } +} + +template <class ELFT> void DefaultLayout<ELFT>::sortInputSections() { + // First, sort according to default layout's order + std::stable_sort( + _sections.begin(), _sections.end(), + [](Chunk<ELFT> *A, Chunk<ELFT> *B) { return A->order() < B->order(); }); + + if (!_linkerScriptSema.hasLayoutCommands()) + return; + + // Sort the sections by their order as defined by the linker script + std::stable_sort(this->_sections.begin(), this->_sections.end(), + [this](Chunk<ELFT> *A, Chunk<ELFT> *B) { + auto *a = dyn_cast<Section<ELFT>>(A); + auto *b = dyn_cast<Section<ELFT>>(B); + + if (a == nullptr) + return false; + if (b == nullptr) + return true; + + return _linkerScriptSema.less( + {a->archivePath(), a->memberPath(), + a->inputSectionName()}, + {b->archivePath(), b->memberPath(), + b->inputSectionName()}); + }); + // Now try to arrange sections with no mapping rules to sections with + // similar content + auto p = this->_sections.begin(); + // Find first section that has no assigned rule id + while (p != this->_sections.end()) { + auto *sect = dyn_cast<AtomSection<ELFT>>(*p); + if (!sect) + break; + + if (!_linkerScriptSema.hasMapping({sect->archivePath(), + sect->memberPath(), + sect->inputSectionName()})) + break; + + ++p; + } + // For all sections that have no assigned rule id, try to move them near a + // section with similar contents + if (p != this->_sections.begin()) { + for (; p != this->_sections.end(); ++p) { + auto q = p; + --q; + while (q != this->_sections.begin() && + (*q)->getContentType() != (*p)->getContentType()) + --q; + if ((*q)->getContentType() != (*p)->getContentType()) + continue; + ++q; + for (auto i = p; i != q;) { + auto next = i--; + std::iter_swap(i, next); + } + } + } +} + +template <class ELFT> +void DefaultLayout<ELFT>::addExtraChunksToSegment(Segment<ELFT> *segment, + StringRef archivePath, + StringRef memberPath, + StringRef sectionName) { + if (!_linkerScriptSema.hasLayoutCommands()) + return; + + std::vector<const script::SymbolAssignment *> exprs = + _linkerScriptSema.getExprs({archivePath, memberPath, sectionName}); + for (auto expr : exprs) { + auto expChunk = + new (this->_allocator) ExpressionChunk<ELFT>(this->_context, expr); + segment->append(expChunk); + } +} + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/DefaultTargetHandler.h b/lib/ReaderWriter/ELF/DefaultTargetHandler.h new file mode 100644 index 0000000000000..16668f2df6182 --- /dev/null +++ b/lib/ReaderWriter/ELF/DefaultTargetHandler.h @@ -0,0 +1,38 @@ +//===- lib/ReaderWriter/ELF/DefaultTargetHandler.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_DEFAULT_TARGET_HANDLER_H +#define LLD_READER_WRITER_ELF_DEFAULT_TARGET_HANDLER_H + +#include "DefaultLayout.h" +#include "DynamicLibraryWriter.h" +#include "ELFReader.h" +#include "ExecutableWriter.h" +#include "TargetHandler.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Support/ELF.h" + +namespace lld { +namespace elf { +template <class ELFT> +class DefaultTargetHandler : public TargetHandler<ELFT> { +public: + const TargetRelocationHandler &getRelocationHandler() const = 0; + + virtual std::unique_ptr<Reader> getObjReader() = 0; + + virtual std::unique_ptr<Reader> getDSOReader() = 0; + + virtual std::unique_ptr<Writer> getWriter() = 0; +}; + +} // end namespace elf +} // end namespace lld +#endif diff --git a/lib/ReaderWriter/ELF/DynamicFile.h b/lib/ReaderWriter/ELF/DynamicFile.h new file mode 100644 index 0000000000000..c4e3e7165efd2 --- /dev/null +++ b/lib/ReaderWriter/ELF/DynamicFile.h @@ -0,0 +1,123 @@ +//===- lib/ReaderWriter/ELF/DynamicFile.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_DYNAMIC_FILE_H +#define LLD_READER_WRITER_ELF_DYNAMIC_FILE_H + +#include "Atoms.h" +#include "lld/Core/SharedLibraryFile.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Path.h" +#include <unordered_map> + +namespace lld { +namespace elf { +template <class ELFT> class DynamicFile : public SharedLibraryFile { +public: + static ErrorOr<std::unique_ptr<DynamicFile>> + create(std::unique_ptr<llvm::MemoryBuffer> mb, ELFLinkingContext &ctx); + + const SharedLibraryAtom *exports(StringRef name, + bool dataSymbolOnly) const override { + assert(!dataSymbolOnly && "Invalid option for ELF exports!"); + // See if we have the symbol. + auto sym = _nameToSym.find(name); + if (sym == _nameToSym.end()) + return nullptr; + // Have we already created a SharedLibraryAtom for it? + if (sym->second._atom) + return sym->second._atom; + // Create a SharedLibraryAtom for this symbol. + return sym->second._atom = new (_alloc) ELFDynamicAtom<ELFT>( + *this, name, _soname, sym->second._symbol); + } + + StringRef getDSOName() const override { return _soname; } + +protected: + std::error_code doParse() override { + std::error_code ec; + _objFile.reset( + new llvm::object::ELFFile<ELFT>(_mb->getBuffer(), ec)); + if (ec) + return ec; + + llvm::object::ELFFile<ELFT> &obj = *_objFile; + + _soname = obj.getLoadName(); + if (_soname.empty()) + _soname = llvm::sys::path::filename(path()); + + // Create a map from names to dynamic symbol table entries. + // TODO: This should use the object file's build in hash table instead if + // it exists. + for (auto i = obj.begin_dynamic_symbols(), e = obj.end_dynamic_symbols(); + i != e; ++i) { + auto name = obj.getSymbolName(i); + if ((ec = name.getError())) + return ec; + + // Dont add local symbols to dynamic entries. The first symbol in the + // dynamic symbol table is a local symbol. + if (i->getBinding() == llvm::ELF::STB_LOCAL) + continue; + + // TODO: Add absolute symbols + if (i->st_shndx == llvm::ELF::SHN_ABS) + continue; + + if (i->st_shndx == llvm::ELF::SHN_UNDEF) { + if (!_useShlibUndefines) + continue; + // Create an undefined atom. + if (!name->empty()) { + auto *newAtom = new (_alloc) ELFUndefinedAtom<ELFT>(*this, *name, &*i); + _undefinedAtoms._atoms.push_back(newAtom); + } + continue; + } + _nameToSym[*name]._symbol = &*i; + } + return std::error_code(); + } + +private: + DynamicFile(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &ctx) + : SharedLibraryFile(mb->getBufferIdentifier()), _mb(std::move(mb)), + _ctx(ctx), _useShlibUndefines(ctx.useShlibUndefines()) {} + + mutable llvm::BumpPtrAllocator _alloc; + std::unique_ptr<llvm::object::ELFFile<ELFT>> _objFile; + /// \brief DT_SONAME + StringRef _soname; + + struct SymAtomPair { + SymAtomPair() : _symbol(nullptr), _atom(nullptr) {} + const typename llvm::object::ELFFile<ELFT>::Elf_Sym *_symbol; + const SharedLibraryAtom *_atom; + }; + + std::unique_ptr<MemoryBuffer> _mb; + ELFLinkingContext &_ctx; + bool _useShlibUndefines; + mutable std::unordered_map<StringRef, SymAtomPair> _nameToSym; +}; + +template <class ELFT> +ErrorOr<std::unique_ptr<DynamicFile<ELFT>>> +DynamicFile<ELFT>::create(std::unique_ptr<llvm::MemoryBuffer> mb, + ELFLinkingContext &ctx) { + return std::unique_ptr<DynamicFile>(new DynamicFile(std::move(mb), ctx)); +} + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/DynamicLibraryWriter.h b/lib/ReaderWriter/ELF/DynamicLibraryWriter.h new file mode 100644 index 0000000000000..f97514b525c0b --- /dev/null +++ b/lib/ReaderWriter/ELF/DynamicLibraryWriter.h @@ -0,0 +1,96 @@ +//===- lib/ReaderWriter/ELF/DynamicLibraryWriter.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_DYNAMIC_LIBRARY_WRITER_H +#define LLD_READER_WRITER_ELF_DYNAMIC_LIBRARY_WRITER_H + +#include "OutputELFWriter.h" + +namespace lld { +namespace elf { +using namespace llvm; +using namespace llvm::object; + +template<class ELFT> +class DynamicLibraryWriter; + +//===----------------------------------------------------------------------===// +// DynamicLibraryWriter Class +//===----------------------------------------------------------------------===// +template<class ELFT> +class DynamicLibraryWriter : public OutputELFWriter<ELFT> { +public: + DynamicLibraryWriter(ELFLinkingContext &context, TargetLayout<ELFT> &layout) + : OutputELFWriter<ELFT>(context, layout), + _runtimeFile(new RuntimeFile<ELFT>(context, "C runtime")) {} + +protected: + virtual void buildDynamicSymbolTable(const File &file); + virtual void addDefaultAtoms(); + virtual bool createImplicitFiles(std::vector<std::unique_ptr<File> > &); + virtual void finalizeDefaultAtomValues(); + +protected: + std::unique_ptr<RuntimeFile<ELFT> > _runtimeFile; +}; + +//===----------------------------------------------------------------------===// +// DynamicLibraryWriter +//===----------------------------------------------------------------------===// +template <class ELFT> +void DynamicLibraryWriter<ELFT>::buildDynamicSymbolTable(const File &file) { + // Add all the defined symbols to the dynamic symbol table + // we need hooks into the Atom to find out which atoms need + // to be exported + for (auto sec : this->_layout.sections()) + if (auto section = dyn_cast<AtomSection<ELFT>>(sec)) + for (const auto &atom : section->atoms()) { + const DefinedAtom *da = dyn_cast<const DefinedAtom>(atom->_atom); + if (da && (da->scope() == DefinedAtom::scopeGlobal)) + this->_dynamicSymbolTable->addSymbol(atom->_atom, section->ordinal(), + atom->_virtualAddr, atom); + } + + for (const UndefinedAtom *a : file.undefined()) + this->_dynamicSymbolTable->addSymbol(a, ELF::SHN_UNDEF); + + OutputELFWriter<ELFT>::buildDynamicSymbolTable(file); +} + +template <class ELFT> void DynamicLibraryWriter<ELFT>::addDefaultAtoms() { + _runtimeFile->addAbsoluteAtom("_end"); +} + +/// \brief Hook in lld to add CRuntime file +template <class ELFT> +bool DynamicLibraryWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File> > &result) { + // Add the default atoms as defined by executables + DynamicLibraryWriter<ELFT>::addDefaultAtoms(); + OutputELFWriter<ELFT>::createImplicitFiles(result); + result.push_back(std::move(_runtimeFile)); + return true; +} + +template <class ELFT> +void DynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues() { + auto underScoreEndAtomIter = this->_layout.findAbsoluteAtom("_end"); + + if (auto bssSection = this->_layout.findOutputSection(".bss")) { + (*underScoreEndAtomIter)->_virtualAddr = + bssSection->virtualAddr() + bssSection->memSize(); + } else if (auto dataSection = this->_layout.findOutputSection(".data")) { + (*underScoreEndAtomIter)->_virtualAddr = + dataSection->virtualAddr() + dataSection->memSize(); + } +} + +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_ELF_DYNAMIC_LIBRARY_WRITER_H diff --git a/lib/ReaderWriter/ELF/ELFFile.h b/lib/ReaderWriter/ELF/ELFFile.h new file mode 100644 index 0000000000000..11f4ee4fc633f --- /dev/null +++ b/lib/ReaderWriter/ELF/ELFFile.h @@ -0,0 +1,1179 @@ +//===- lib/ReaderWriter/ELF/ELFFile.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_FILE_H +#define LLD_READER_WRITER_ELF_FILE_H + +#include "Atoms.h" +#include <llvm/ADT/MapVector.h> +#include <map> +#include <unordered_map> + +namespace lld { + +namespace elf { +/// \brief Read a binary, find out based on the symbol table contents what kind +/// of symbol it is and create corresponding atoms for it +template <class ELFT> class ELFFile : public File { + + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + typedef llvm::object::Elf_Rel_Impl<ELFT, false> Elf_Rel; + typedef llvm::object::Elf_Rel_Impl<ELFT, true> Elf_Rela; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Sym_Iter Elf_Sym_Iter; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Rela_Iter Elf_Rela_Iter; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Rel_Iter Elf_Rel_Iter; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Word Elf_Word; + + // A Map is used to hold the atoms that have been divided up + // after reading the section that contains Merge String attributes + struct MergeSectionKey { + MergeSectionKey(const Elf_Shdr *shdr, int64_t offset) + : _shdr(shdr), _offset(offset) {} + // Data members + const Elf_Shdr *_shdr; + int64_t _offset; + }; + struct MergeSectionEq { + int64_t operator()(const MergeSectionKey &k) const { + return llvm::hash_combine((int64_t)(k._shdr->sh_name), + (int64_t)k._offset); + } + bool operator()(const MergeSectionKey &lhs, + const MergeSectionKey &rhs) const { + return ((lhs._shdr->sh_name == rhs._shdr->sh_name) && + (lhs._offset == rhs._offset)); + } + }; + + struct MergeString { + MergeString(int64_t offset, StringRef str, const Elf_Shdr *shdr, + StringRef sectionName) + : _offset(offset), _string(str), _shdr(shdr), + _sectionName(sectionName) {} + // the offset of this atom + int64_t _offset; + // The content + StringRef _string; + // Section header + const Elf_Shdr *_shdr; + // Section name + StringRef _sectionName; + }; + + // This is used to find the MergeAtom given a relocation + // offset + typedef std::vector<ELFMergeAtom<ELFT> *> MergeAtomsT; + + /// \brief find a mergeAtom given a start offset + struct FindByOffset { + const Elf_Shdr *_shdr; + int64_t _offset; + FindByOffset(const Elf_Shdr *shdr, int64_t offset) + : _shdr(shdr), _offset(offset) {} + bool operator()(const ELFMergeAtom<ELFT> *a) { + int64_t off = a->offset(); + return (_shdr->sh_name == a->section()) && + ((_offset >= off) && (_offset <= off + (int64_t)a->size())); + } + }; + + /// \brief find a merge atom given a offset + ELFMergeAtom<ELFT> *findMergeAtom(const Elf_Shdr *shdr, uint64_t offset) { + auto it = std::find_if(_mergeAtoms.begin(), _mergeAtoms.end(), + FindByOffset(shdr, offset)); + assert(it != _mergeAtoms.end()); + return *it; + } + + typedef std::unordered_map<MergeSectionKey, DefinedAtom *, MergeSectionEq, + MergeSectionEq> MergedSectionMapT; + typedef typename MergedSectionMapT::iterator MergedSectionMapIterT; + +public: + ELFFile(StringRef name, ELFLinkingContext &ctx) + : File(name, kindObject), _ordinal(0), + _doStringsMerge(ctx.mergeCommonStrings()), _useWrap(false), _ctx(ctx) { + setLastError(std::error_code()); + } + + ELFFile(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &ctx) + : File(mb->getBufferIdentifier(), kindObject), _mb(std::move(mb)), + _ordinal(0), _doStringsMerge(ctx.mergeCommonStrings()), + _useWrap(ctx.wrapCalls().size()), _ctx(ctx) {} + + static ErrorOr<std::unique_ptr<ELFFile>> + create(std::unique_ptr<MemoryBuffer> mb, ELFLinkingContext &ctx); + + virtual Reference::KindArch kindArch(); + + /// \brief Create symbols from LinkingContext. + std::error_code createAtomsFromContext(); + + /// \brief Read input sections and populate necessary data structures + /// to read them later and create atoms + std::error_code createAtomizableSections(); + + /// \brief Create mergeable atoms from sections that have the merge attribute + /// set + std::error_code createMergeableAtoms(); + + /// \brief Add the symbols that the sections contain. The symbols will be + /// converted to atoms for + /// Undefined symbols, absolute symbols + std::error_code createSymbolsFromAtomizableSections(); + + /// \brief Create individual atoms + std::error_code createAtoms(); + + 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; + } + + Atom *findAtom(const Elf_Sym *sourceSymbol, const Elf_Sym *targetSymbol) { + // All references to atoms inside a group are through undefined atoms. + Atom *targetAtom = _symbolToAtomMapping.lookup(targetSymbol); + StringRef targetSymbolName = targetAtom->name(); + if (targetAtom->definition() != Atom::definitionRegular) + return targetAtom; + if ((llvm::dyn_cast<DefinedAtom>(targetAtom))->scope() == + DefinedAtom::scopeTranslationUnit) + return targetAtom; + if (!redirectReferenceUsingUndefAtom(sourceSymbol, targetSymbol)) + return targetAtom; + auto undefForGroupchild = _undefAtomsForGroupChild.find(targetSymbolName); + if (undefForGroupchild != _undefAtomsForGroupChild.end()) + return undefForGroupchild->getValue(); + auto undefGroupChildAtom = + new (_readerStorage) SimpleUndefinedAtom(*this, targetSymbolName); + _undefinedAtoms._atoms.push_back(undefGroupChildAtom); + return (_undefAtomsForGroupChild[targetSymbolName] = undefGroupChildAtom); + } + +protected: + ELFDefinedAtom<ELFT> *createDefinedAtomAndAssignRelocations( + StringRef symbolName, StringRef sectionName, const Elf_Sym *symbol, + const Elf_Shdr *section, ArrayRef<uint8_t> symContent, + ArrayRef<uint8_t> secContent); + + std::error_code doParse() override; + + /// \brief Iterate over Elf_Rela relocations list and create references. + virtual void createRelocationReferences(const Elf_Sym *symbol, + ArrayRef<uint8_t> content, + range<Elf_Rela_Iter> rels); + + /// \brief Iterate over Elf_Rel relocations list and create references. + virtual void createRelocationReferences(const Elf_Sym *symbol, + ArrayRef<uint8_t> symContent, + ArrayRef<uint8_t> secContent, + range<Elf_Rel_Iter> rels); + + /// \brief After all the Atoms and References are created, update each + /// Reference's target with the Atom pointer it refers to. + void updateReferences(); + + /// \brief Update the reference if the access corresponds to a merge string + /// section. + void updateReferenceForMergeStringAccess(ELFReference<ELFT> *ref, + const Elf_Sym *symbol, + const Elf_Shdr *shdr); + + /// \brief Do we want to ignore the section. Ignored sections are + /// not processed to create atoms + bool isIgnoredSection(const Elf_Shdr *section); + + /// \brief Is the current section be treated as a mergeable string section. + /// The contents of a mergeable string section are null-terminated strings. + /// If the section have mergeable strings, the linker would need to split + /// the section into multiple atoms and mark them mergeByContent. + bool isMergeableStringSection(const Elf_Shdr *section); + + /// \brief Returns a new anonymous atom whose size is equal to the + /// section size. That atom will be used to represent the entire + /// section that have no symbols. + ELFDefinedAtom<ELFT> *createSectionAtom(const Elf_Shdr *section, + StringRef sectionName, + ArrayRef<uint8_t> contents); + + /// Returns the symbol's content size. The nextSymbol should be null if the + /// symbol is the last one in the section. + uint64_t symbolContentSize(const Elf_Shdr *section, + const Elf_Sym *symbol, + const Elf_Sym *nextSymbol); + + void createEdge(ELFDefinedAtom<ELFT> *from, ELFDefinedAtom<ELFT> *to, + uint32_t edgeKind); + + /// Get the section name for a section. + ErrorOr<StringRef> getSectionName(const Elf_Shdr *shdr) const { + if (!shdr) + return StringRef(); + return _objFile->getSectionName(shdr); + } + + /// Determines if the section occupy memory space. + bool sectionOccupiesMemorySpace(const Elf_Shdr *shdr) const { + return (shdr->sh_type != llvm::ELF::SHT_NOBITS); + } + + /// Return the section contents. + ErrorOr<ArrayRef<uint8_t>> getSectionContents(const Elf_Shdr *shdr) const { + if (!shdr || !sectionOccupiesMemorySpace(shdr)) + return ArrayRef<uint8_t>(); + return _objFile->getSectionContents(shdr); + } + + /// Returns true if the symbol is a undefined symbol. + bool isUndefinedSymbol(const Elf_Sym *sym) const { + return (sym->st_shndx == llvm::ELF::SHN_UNDEF); + } + + /// Determines if the target wants to create an atom for a section that has no + /// symbol references. + bool handleSectionWithNoSymbols(const Elf_Shdr *shdr, + std::vector<Elf_Sym_Iter> &syms) const { + return shdr && (shdr->sh_type == llvm::ELF::SHT_PROGBITS) && syms.empty(); + } + + /// Handle creation of atoms for .gnu.linkonce sections. + std::error_code handleGnuLinkOnceSection( + StringRef sectionName, + llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection, + const Elf_Shdr *shdr); + + // Handle Section groups/COMDAT scetions. + std::error_code handleSectionGroup( + StringRef signature, StringRef groupSectionName, + llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection, + llvm::DenseMap<const Elf_Shdr *, std::vector<StringRef>> &comdatSections, + const Elf_Shdr *shdr); + + /// Process the Undefined symbol and create an atom for it. + ErrorOr<ELFUndefinedAtom<ELFT> *> + handleUndefinedSymbol(StringRef symName, const Elf_Sym *sym) { + return new (_readerStorage) ELFUndefinedAtom<ELFT>(*this, symName, sym); + } + + /// Returns true if the symbol is a absolute symbol. + bool isAbsoluteSymbol(const Elf_Sym *sym) const { + return (sym->st_shndx == llvm::ELF::SHN_ABS); + } + + /// Process the Absolute symbol and create an atom for it. + ErrorOr<ELFAbsoluteAtom<ELFT> *> + handleAbsoluteSymbol(StringRef symName, const Elf_Sym *sym, int64_t value) { + return new (_readerStorage) + ELFAbsoluteAtom<ELFT>(*this, symName, sym, value); + } + + /// Returns true if the symbol is common symbol. A common symbol represents a + /// tentive definition in C. It has name, size and alignment constraint, but + /// actual storage has not yet been allocated. (The linker will allocate + /// storage for them in the later pass after coalescing tentative symbols by + /// name.) + virtual bool isCommonSymbol(const Elf_Sym *symbol) const { + return symbol->getType() == llvm::ELF::STT_COMMON || + symbol->st_shndx == llvm::ELF::SHN_COMMON; + } + + /// Returns true if the section is a gnulinkonce section. + bool isGnuLinkOnceSection(StringRef sectionName) const { + return sectionName.startswith(".gnu.linkonce."); + } + + /// Returns true if the section is a COMDAT group section. + bool isGroupSection(const Elf_Shdr *shdr) const { + return (shdr->sh_type == llvm::ELF::SHT_GROUP); + } + + /// Returns true if the section is a member of some group. + bool isSectionMemberOfGroup(const Elf_Shdr *shdr) const { + return (shdr->sh_flags & llvm::ELF::SHF_GROUP); + } + + /// Returns correct st_value for the symbol depending on the architecture. + /// For most architectures it's just a regular st_value with no changes. + virtual uint64_t getSymbolValue(const Elf_Sym *symbol) const { + return symbol->st_value; + } + + /// Process the common symbol and create an atom for it. + virtual ErrorOr<ELFCommonAtom<ELFT> *> + handleCommonSymbol(StringRef symName, const Elf_Sym *sym) { + return new (_readerStorage) ELFCommonAtom<ELFT>(*this, symName, sym); + } + + /// Returns true if the symbol is a defined symbol. + virtual bool isDefinedSymbol(const Elf_Sym *sym) const { + return (sym->getType() == llvm::ELF::STT_NOTYPE || + sym->getType() == llvm::ELF::STT_OBJECT || + sym->getType() == llvm::ELF::STT_FUNC || + sym->getType() == llvm::ELF::STT_GNU_IFUNC || + sym->getType() == llvm::ELF::STT_SECTION || + sym->getType() == llvm::ELF::STT_FILE || + sym->getType() == llvm::ELF::STT_TLS); + } + + /// Process the Defined symbol and create an atom for it. + virtual ErrorOr<ELFDefinedAtom<ELFT> *> + handleDefinedSymbol(StringRef symName, StringRef sectionName, + const Elf_Sym *sym, const Elf_Shdr *sectionHdr, + ArrayRef<uint8_t> contentData, + unsigned int referenceStart, unsigned int referenceEnd, + std::vector<ELFReference<ELFT> *> &referenceList) { + return new (_readerStorage) ELFDefinedAtom<ELFT>( + *this, symName, sectionName, sym, sectionHdr, contentData, + referenceStart, referenceEnd, referenceList); + } + + /// Process the Merge string and create an atom for it. + ErrorOr<ELFMergeAtom<ELFT> *> + handleMergeString(StringRef sectionName, const Elf_Shdr *sectionHdr, + ArrayRef<uint8_t> contentData, unsigned int offset) { + ELFMergeAtom<ELFT> *mergeAtom = new (_readerStorage) + ELFMergeAtom<ELFT>(*this, sectionName, sectionHdr, contentData, offset); + const MergeSectionKey mergedSectionKey(sectionHdr, offset); + if (_mergedSectionMap.find(mergedSectionKey) == _mergedSectionMap.end()) + _mergedSectionMap.insert(std::make_pair(mergedSectionKey, mergeAtom)); + return mergeAtom; + } + + /// References to the sections comprising a group, from sections + /// outside the group, must be made via global UNDEF symbols, + /// referencing global symbols defined as addresses in the group + /// sections. They may not reference local symbols for addresses in + /// the group's sections, including section symbols. + /// ABI Doc : https://mentorembedded.github.io/cxx-abi/abi/prop-72-comdat.html + /// Does the atom need to be redirected using a separate undefined atom? + bool redirectReferenceUsingUndefAtom(const Elf_Sym *sourceSymbol, + const Elf_Sym *targetSymbol) const; + + void addReferenceToSymbol(const ELFReference<ELFT> *r, const Elf_Sym *sym) { + _referenceToSymbol[r] = sym; + } + + const Elf_Sym *findSymbolForReference(const ELFReference<ELFT> *r) const { + auto elfReferenceToSymbol = _referenceToSymbol.find(r); + if (elfReferenceToSymbol != _referenceToSymbol.end()) + return elfReferenceToSymbol->second; + return nullptr; + } + + llvm::BumpPtrAllocator _readerStorage; + std::unique_ptr<llvm::object::ELFFile<ELFT> > _objFile; + atom_collection_vector<DefinedAtom> _definedAtoms; + atom_collection_vector<UndefinedAtom> _undefinedAtoms; + atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms; + atom_collection_vector<AbsoluteAtom> _absoluteAtoms; + + /// \brief _relocationAddendReferences and _relocationReferences contain the + /// list of relocations references. In ELF, if a section named, ".text" has + /// relocations will also have a section named ".rel.text" or ".rela.text" + /// which will hold the entries. + std::unordered_map<StringRef, range<Elf_Rela_Iter>> + _relocationAddendReferences; + MergedSectionMapT _mergedSectionMap; + std::unordered_map<StringRef, range<Elf_Rel_Iter>> _relocationReferences; + std::vector<ELFReference<ELFT> *> _references; + llvm::DenseMap<const Elf_Sym *, Atom *> _symbolToAtomMapping; + llvm::DenseMap<const ELFReference<ELFT> *, const Elf_Sym *> + _referenceToSymbol; + // Group child atoms have a pair corresponding to the signature and the + // section header of the section that was used for generating the signature. + llvm::DenseMap<const Elf_Sym *, std::pair<StringRef, const Elf_Shdr *>> + _groupChild; + llvm::StringMap<Atom *> _undefAtomsForGroupChild; + + /// \brief Atoms that are created for a section that has the merge property + /// set + MergeAtomsT _mergeAtoms; + + /// \brief the section and the symbols that are contained within it to create + /// used to create atoms + llvm::MapVector<const Elf_Shdr *, std::vector<Elf_Sym_Iter>> _sectionSymbols; + + /// \brief Sections that have merge string property + std::vector<const Elf_Shdr *> _mergeStringSections; + + std::unique_ptr<MemoryBuffer> _mb; + int64_t _ordinal; + + /// \brief the cached options relevant while reading the ELF File + bool _doStringsMerge; + + /// \brief Is --wrap on? + bool _useWrap; + + /// \brief The LinkingContext. + ELFLinkingContext &_ctx; + + // Wrap map + llvm::StringMap<UndefinedAtom *> _wrapSymbolMap; +}; + +/// \brief All atoms are owned by a File. To add linker specific atoms +/// the atoms need to be inserted to a file called (RuntimeFile) which +/// are basically additional symbols required by libc and other runtime +/// libraries part of executing a program. This class provides support +/// for adding absolute symbols and undefined symbols +template <class ELFT> class RuntimeFile : public ELFFile<ELFT> { +public: + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + RuntimeFile(ELFLinkingContext &context, StringRef name) + : ELFFile<ELFT>(name, context) {} + + /// \brief add a global absolute atom + virtual Atom *addAbsoluteAtom(StringRef symbolName) { + assert(!symbolName.empty() && "AbsoluteAtoms must have a name"); + Elf_Sym *symbol = new (this->_readerStorage) Elf_Sym; + symbol->st_name = 0; + symbol->st_value = 0; + symbol->st_shndx = llvm::ELF::SHN_ABS; + symbol->setBindingAndType(llvm::ELF::STB_GLOBAL, llvm::ELF::STT_OBJECT); + symbol->setVisibility(llvm::ELF::STV_DEFAULT); + symbol->st_size = 0; + auto newAtom = this->handleAbsoluteSymbol(symbolName, symbol, -1); + this->_absoluteAtoms._atoms.push_back(*newAtom); + return *newAtom; + } + + /// \brief add an undefined atom + virtual Atom *addUndefinedAtom(StringRef symbolName) { + assert(!symbolName.empty() && "UndefinedAtoms must have a name"); + Elf_Sym *symbol = new (this->_readerStorage) Elf_Sym; + symbol->st_name = 0; + symbol->st_value = 0; + symbol->st_shndx = llvm::ELF::SHN_UNDEF; + symbol->setBindingAndType(llvm::ELF::STB_GLOBAL, llvm::ELF::STT_NOTYPE); + symbol->setVisibility(llvm::ELF::STV_DEFAULT); + symbol->st_size = 0; + auto newAtom = this->handleUndefinedSymbol(symbolName, symbol); + this->_undefinedAtoms._atoms.push_back(*newAtom); + return *newAtom; + } + + // cannot add atoms to Runtime file + virtual void addAtom(const Atom &) { + llvm_unreachable("cannot add atoms to Runtime files"); + } +}; + +template <class ELFT> +ErrorOr<std::unique_ptr<ELFFile<ELFT>>> +ELFFile<ELFT>::create(std::unique_ptr<MemoryBuffer> mb, + ELFLinkingContext &ctx) { + std::unique_ptr<ELFFile<ELFT>> file(new ELFFile<ELFT>(std::move(mb), ctx)); + return std::move(file); +} + +template <class ELFT> +std::error_code ELFFile<ELFT>::doParse() { + std::error_code ec; + _objFile.reset(new llvm::object::ELFFile<ELFT>(_mb->getBuffer(), ec)); + if (ec) + return ec; + + if ((ec = createAtomsFromContext())) + return ec; + + // Read input sections from the input file that need to be converted to + // atoms + if ((ec = createAtomizableSections())) + return ec; + + // For mergeable strings, we would need to split the section into various + // atoms + if ((ec = createMergeableAtoms())) + return ec; + + // Create the necessary symbols that are part of the section that we + // created in createAtomizableSections function + if ((ec = createSymbolsFromAtomizableSections())) + return ec; + + // Create the appropriate atoms from the file + if ((ec = createAtoms())) + return ec; + return std::error_code(); +} + +template <class ELFT> Reference::KindArch ELFFile<ELFT>::kindArch() { + switch (_objFile->getHeader()->e_machine) { + case llvm::ELF::EM_X86_64: + return Reference::KindArch::x86_64; + case llvm::ELF::EM_386: + return Reference::KindArch::x86; + case llvm::ELF::EM_ARM: + return Reference::KindArch::ARM; + case llvm::ELF::EM_HEXAGON: + return Reference::KindArch::Hexagon; + case llvm::ELF::EM_MIPS: + return Reference::KindArch::Mips; + case llvm::ELF::EM_AARCH64: + return Reference::KindArch::AArch64; + } + llvm_unreachable("unsupported e_machine value"); +} + +template <class ELFT> +std::error_code ELFFile<ELFT>::createAtomizableSections() { + // Handle: SHT_REL and SHT_RELA sections: + // Increment over the sections, when REL/RELA section types are found add + // the contents to the RelocationReferences map. + // Record the number of relocs to guess at preallocating the buffer. + uint64_t totalRelocs = 0; + for (const Elf_Shdr §ion : _objFile->sections()) { + if (isIgnoredSection(§ion)) + continue; + + if (isMergeableStringSection(§ion)) { + _mergeStringSections.push_back(§ion); + continue; + } + + if (section.sh_type == llvm::ELF::SHT_RELA) { + auto sHdr = _objFile->getSection(section.sh_info); + + auto sectionName = _objFile->getSectionName(sHdr); + if (std::error_code ec = sectionName.getError()) + return ec; + + auto rai(_objFile->begin_rela(§ion)); + auto rae(_objFile->end_rela(§ion)); + + _relocationAddendReferences[*sectionName] = make_range(rai, rae); + totalRelocs += std::distance(rai, rae); + } else if (section.sh_type == llvm::ELF::SHT_REL) { + auto sHdr = _objFile->getSection(section.sh_info); + + auto sectionName = _objFile->getSectionName(sHdr); + if (std::error_code ec = sectionName.getError()) + return ec; + + auto ri(_objFile->begin_rel(§ion)); + auto re(_objFile->end_rel(§ion)); + + _relocationReferences[*sectionName] = make_range(ri, re); + totalRelocs += std::distance(ri, re); + } else { + _sectionSymbols[§ion]; + } + } + _references.reserve(totalRelocs); + return std::error_code(); +} + +template <class ELFT> std::error_code ELFFile<ELFT>::createMergeableAtoms() { + // Divide the section that contains mergeable strings into tokens + // TODO + // a) add resolver support to recognize multibyte chars + // b) Create a separate section chunk to write mergeable atoms + std::vector<MergeString *> tokens; + for (const Elf_Shdr *msi : _mergeStringSections) { + auto sectionName = getSectionName(msi); + if (std::error_code ec = sectionName.getError()) + return ec; + + auto sectionContents = getSectionContents(msi); + if (std::error_code ec = sectionContents.getError()) + return ec; + + StringRef secCont(reinterpret_cast<const char *>(sectionContents->begin()), + sectionContents->size()); + + unsigned int prev = 0; + for (std::size_t i = 0, e = sectionContents->size(); i != e; ++i) { + if ((*sectionContents)[i] == '\0') { + tokens.push_back(new (_readerStorage) MergeString( + prev, secCont.slice(prev, i + 1), msi, *sectionName)); + prev = i + 1; + } + } + } + + // Create Mergeable atoms + for (const MergeString *tai : tokens) { + ArrayRef<uint8_t> content((const uint8_t *)tai->_string.data(), + tai->_string.size()); + ErrorOr<ELFMergeAtom<ELFT> *> mergeAtom = + handleMergeString(tai->_sectionName, tai->_shdr, content, tai->_offset); + (*mergeAtom)->setOrdinal(++_ordinal); + _definedAtoms._atoms.push_back(*mergeAtom); + _mergeAtoms.push_back(*mergeAtom); + } + return std::error_code(); +} + +template <class ELFT> +std::error_code ELFFile<ELFT>::createSymbolsFromAtomizableSections() { + // Increment over all the symbols collecting atoms and symbol names for + // later use. + auto SymI = _objFile->begin_symbols(), SymE = _objFile->end_symbols(); + + // Skip over dummy sym. + if (SymI != SymE) + ++SymI; + + for (; SymI != SymE; ++SymI) { + const Elf_Shdr *section = _objFile->getSection(&*SymI); + + auto symbolName = _objFile->getSymbolName(SymI); + if (std::error_code ec = symbolName.getError()) + return ec; + + if (isAbsoluteSymbol(&*SymI)) { + ErrorOr<ELFAbsoluteAtom<ELFT> *> absAtom = + handleAbsoluteSymbol(*symbolName, &*SymI, (int64_t)getSymbolValue(&*SymI)); + _absoluteAtoms._atoms.push_back(*absAtom); + _symbolToAtomMapping.insert(std::make_pair(&*SymI, *absAtom)); + } else if (isUndefinedSymbol(&*SymI)) { + if (_useWrap && + (_wrapSymbolMap.find(*symbolName) != _wrapSymbolMap.end())) { + auto wrapAtom = _wrapSymbolMap.find(*symbolName); + _symbolToAtomMapping.insert( + std::make_pair(&*SymI, wrapAtom->getValue())); + continue; + } + ErrorOr<ELFUndefinedAtom<ELFT> *> undefAtom = + handleUndefinedSymbol(*symbolName, &*SymI); + _undefinedAtoms._atoms.push_back(*undefAtom); + _symbolToAtomMapping.insert(std::make_pair(&*SymI, *undefAtom)); + } else if (isCommonSymbol(&*SymI)) { + ErrorOr<ELFCommonAtom<ELFT> *> commonAtom = + handleCommonSymbol(*symbolName, &*SymI); + (*commonAtom)->setOrdinal(++_ordinal); + _definedAtoms._atoms.push_back(*commonAtom); + _symbolToAtomMapping.insert(std::make_pair(&*SymI, *commonAtom)); + } else if (isDefinedSymbol(&*SymI)) { + _sectionSymbols[section].push_back(SymI); + } else { + llvm::errs() << "Unable to create atom for: " << *symbolName << "\n"; + return llvm::object::object_error::parse_failed; + } + } + + return std::error_code(); +} + +template <class ELFT> std::error_code ELFFile<ELFT>::createAtoms() { + // Holds all the atoms that are part of the section. They are the targets of + // the kindGroupChild reference. + llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> atomsForSection; + // group sections have a mapping of the section header to the + // signature/section. + llvm::DenseMap<const Elf_Shdr *, std::pair<StringRef, StringRef>> + groupSections; + // Contains a list of comdat sections for a group. + llvm::DenseMap<const Elf_Shdr *, std::vector<StringRef>> comdatSections; + for (auto &i : _sectionSymbols) { + const Elf_Shdr *section = i.first; + std::vector<Elf_Sym_Iter> &symbols = i.second; + + // Sort symbols by position. + std::stable_sort(symbols.begin(), symbols.end(), + [this](Elf_Sym_Iter a, Elf_Sym_Iter b) { + return getSymbolValue(&*a) < getSymbolValue(&*b); + }); + + ErrorOr<StringRef> sectionName = this->getSectionName(section); + if (std::error_code ec = sectionName.getError()) + return ec; + + auto sectionContents = getSectionContents(section); + if (std::error_code ec = sectionContents.getError()) + return ec; + + bool addAtoms = true; + + // A section of type SHT_GROUP defines a grouping of sections. The name of a + // symbol from one of the containing object's symbol tables provides a + // signature + // for the section group. The section header of the SHT_GROUP section + // specifies + // the identifying symbol entry, as described : the sh_link member contains + // the section header index of the symbol table section that contains the + // entry. + // The sh_info member contains the symbol table index of the identifying + // entry. + // The sh_flags member of the section header contains 0. The name of the + // section + // (sh_name) is not specified. + if (isGroupSection(section)) { + const Elf_Word *groupMembers = + reinterpret_cast<const Elf_Word *>(sectionContents->data()); + const long count = (section->sh_size) / sizeof(Elf_Word); + for (int i = 1; i < count; i++) { + const Elf_Shdr *sHdr = _objFile->getSection(groupMembers[i]); + ErrorOr<StringRef> sectionName = _objFile->getSectionName(sHdr); + if (std::error_code ec = sectionName.getError()) + return ec; + comdatSections[section].push_back(*sectionName); + } + const Elf_Sym *symbol = _objFile->getSymbol(section->sh_info); + const Elf_Shdr *symtab = _objFile->getSection(section->sh_link); + ErrorOr<StringRef> symbolName = _objFile->getSymbolName(symtab, symbol); + if (std::error_code ec = symbolName.getError()) + return ec; + groupSections.insert( + std::make_pair(section, std::make_pair(*symbolName, *sectionName))); + continue; + } + + if (isGnuLinkOnceSection(*sectionName)) { + groupSections.insert( + std::make_pair(section, std::make_pair(*sectionName, *sectionName))); + addAtoms = false; + } + + if (isSectionMemberOfGroup(section)) + addAtoms = false; + + if (handleSectionWithNoSymbols(section, symbols)) { + ELFDefinedAtom<ELFT> *newAtom = + createSectionAtom(section, *sectionName, *sectionContents); + newAtom->setOrdinal(++_ordinal); + if (addAtoms) + _definedAtoms._atoms.push_back(newAtom); + else + atomsForSection[*sectionName].push_back(newAtom); + continue; + } + + ELFDefinedAtom<ELFT> *previousAtom = nullptr; + ELFReference<ELFT> *anonFollowedBy = nullptr; + + for (auto si = symbols.begin(), se = symbols.end(); si != se; ++si) { + auto symbol = *si; + StringRef symbolName = ""; + if (symbol->getType() != llvm::ELF::STT_SECTION) { + auto symName = _objFile->getSymbolName(symbol); + if (std::error_code ec = symName.getError()) + return ec; + symbolName = *symName; + } + + uint64_t contentSize = symbolContentSize( + section, &*symbol, (si + 1 == se) ? nullptr : &**(si + 1)); + + // Check to see if we need to add the FollowOn Reference + ELFReference<ELFT> *followOn = nullptr; + if (previousAtom) { + // Replace the followon atom with the anonymous atom that we created, + // so that the next symbol that we create is a followon from the + // anonymous atom. + if (anonFollowedBy) { + followOn = anonFollowedBy; + } else { + followOn = new (_readerStorage) + ELFReference<ELFT>(lld::Reference::kindLayoutAfter); + previousAtom->addReference(followOn); + } + } + + ArrayRef<uint8_t> symbolData((const uint8_t *)sectionContents->data() + + getSymbolValue(&*symbol), + contentSize); + + // If the linker finds that a section has global atoms that are in a + // mergeable section, treat them as defined atoms as they shouldn't be + // merged away as well as these symbols have to be part of symbol + // resolution + if (isMergeableStringSection(section)) { + if (symbol->getBinding() == llvm::ELF::STB_GLOBAL) { + auto definedMergeAtom = handleDefinedSymbol( + symbolName, *sectionName, &**si, section, symbolData, + _references.size(), _references.size(), _references); + (*definedMergeAtom)->setOrdinal(++_ordinal); + if (addAtoms) + _definedAtoms._atoms.push_back(*definedMergeAtom); + else + atomsForSection[*sectionName].push_back(*definedMergeAtom); + } + continue; + } + + // Don't allocate content to a weak symbol, as they may be merged away. + // Create an anonymous atom to hold the data. + ELFDefinedAtom<ELFT> *anonAtom = nullptr; + anonFollowedBy = nullptr; + if (symbol->getBinding() == llvm::ELF::STB_WEAK) { + // Create anonymous new non-weak ELF symbol that holds the symbol + // data. + auto sym = new (_readerStorage) Elf_Sym(*symbol); + sym->setBinding(llvm::ELF::STB_GLOBAL); + anonAtom = createDefinedAtomAndAssignRelocations( + "", *sectionName, sym, section, symbolData, *sectionContents); + symbolData = ArrayRef<uint8_t>(); + + // If this is the last atom, let's not create a followon reference. + if (anonAtom && (si + 1) != se) { + anonFollowedBy = new (_readerStorage) + ELFReference<ELFT>(lld::Reference::kindLayoutAfter); + anonAtom->addReference(anonFollowedBy); + } + } + + ELFDefinedAtom<ELFT> *newAtom = createDefinedAtomAndAssignRelocations( + symbolName, *sectionName, &*symbol, section, symbolData, + *sectionContents); + newAtom->setOrdinal(++_ordinal); + + // If the atom was a weak symbol, let's create a followon reference to + // the anonymous atom that we created. + if (anonAtom) + createEdge(newAtom, anonAtom, Reference::kindLayoutAfter); + + if (previousAtom) { + // Set the followon atom to the weak atom that we have created, so + // that they would alias when the file gets written. + followOn->setTarget(anonAtom ? anonAtom : newAtom); + } + + // The previous atom is always the atom created before unless the atom + // is a weak atom. + previousAtom = anonAtom ? anonAtom : newAtom; + + if (addAtoms) + _definedAtoms._atoms.push_back(newAtom); + else + atomsForSection[*sectionName].push_back(newAtom); + + _symbolToAtomMapping.insert(std::make_pair(&*symbol, newAtom)); + if (anonAtom) { + anonAtom->setOrdinal(++_ordinal); + if (addAtoms) + _definedAtoms._atoms.push_back(anonAtom); + else + atomsForSection[*sectionName].push_back(anonAtom); + } + } + } + + // Iterate over all the group sections to create parent atoms pointing to + // group-child atoms. + for (auto § : groupSections) { + StringRef signature = sect.second.first; + StringRef groupSectionName = sect.second.second; + if (isGnuLinkOnceSection(signature)) + handleGnuLinkOnceSection(signature, atomsForSection, sect.first); + else if (isGroupSection(sect.first)) + handleSectionGroup(signature, groupSectionName, atomsForSection, + comdatSections, sect.first); + } + + updateReferences(); + return std::error_code(); +} + +template <class ELFT> +std::error_code ELFFile<ELFT>::handleGnuLinkOnceSection( + StringRef signature, + llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection, + const Elf_Shdr *shdr) { + // TODO: Check for errors. + unsigned int referenceStart = _references.size(); + std::vector<ELFReference<ELFT> *> refs; + for (auto ha : atomsForSection[signature]) { + _groupChild[ha->symbol()] = std::make_pair(signature, shdr); + ELFReference<ELFT> *ref = + new (_readerStorage) ELFReference<ELFT>(lld::Reference::kindGroupChild); + ref->setTarget(ha); + refs.push_back(ref); + } + atomsForSection[signature].clear(); + // Create a gnu linkonce atom. + auto gnuLinkOnceAtom = handleDefinedSymbol( + signature, signature, nullptr, shdr, ArrayRef<uint8_t>(), referenceStart, + _references.size(), _references); + (*gnuLinkOnceAtom)->setOrdinal(++_ordinal); + _definedAtoms._atoms.push_back(*gnuLinkOnceAtom); + for (auto reference : refs) + (*gnuLinkOnceAtom)->addReference(reference); + return std::error_code(); +} + +template <class ELFT> +std::error_code ELFFile<ELFT>::handleSectionGroup( + StringRef signature, StringRef groupSectionName, + llvm::StringMap<std::vector<ELFDefinedAtom<ELFT> *>> &atomsForSection, + llvm::DenseMap<const Elf_Shdr *, std::vector<StringRef>> &comdatSections, + const Elf_Shdr *shdr) { + // TODO: Check for errors. + unsigned int referenceStart = _references.size(); + std::vector<ELFReference<ELFT> *> refs; + auto sectionNamesInGroup = comdatSections[shdr]; + for (auto sectionName : sectionNamesInGroup) { + for (auto ha : atomsForSection[sectionName]) { + _groupChild[ha->symbol()] = std::make_pair(signature, shdr); + ELFReference<ELFT> *ref = new (_readerStorage) + ELFReference<ELFT>(lld::Reference::kindGroupChild); + ref->setTarget(ha); + refs.push_back(ref); + } + atomsForSection[sectionName].clear(); + } + // Create a gnu linkonce atom. + auto sectionGroupAtom = handleDefinedSymbol( + signature, groupSectionName, nullptr, shdr, ArrayRef<uint8_t>(), + referenceStart, _references.size(), _references); + (*sectionGroupAtom)->setOrdinal(++_ordinal); + _definedAtoms._atoms.push_back(*sectionGroupAtom); + for (auto reference : refs) + (*sectionGroupAtom)->addReference(reference); + return std::error_code(); +} + +template <class ELFT> std::error_code ELFFile<ELFT>::createAtomsFromContext() { + if (!_useWrap) + return std::error_code(); + // Steps :- + // a) Create an undefined atom for the symbol specified by the --wrap option, + // as that + // may be needed to be pulled from an archive. + // b) Create an undefined atom for __wrap_<symbolname>. + // c) All references to the symbol specified by wrap should point to + // __wrap_<symbolname> + // d) All references to __real_symbol should point to the <symbol> + for (auto &wrapsym : _ctx.wrapCalls()) { + StringRef wrapStr = wrapsym.getKey(); + // Create a undefined symbol fror the wrap symbol. + UndefinedAtom *wrapSymAtom = + new (_readerStorage) SimpleUndefinedAtom(*this, wrapStr); + StringRef wrapCallSym = + _ctx.allocateString((llvm::Twine("__wrap_") + wrapStr).str()); + StringRef realCallSym = + _ctx.allocateString((llvm::Twine("__real_") + wrapStr).str()); + UndefinedAtom *wrapCallAtom = + new (_readerStorage) SimpleUndefinedAtom(*this, wrapCallSym); + // Create maps, when there is call to sym, it should point to wrapCallSym. + _wrapSymbolMap.insert(std::make_pair(wrapStr, wrapCallAtom)); + // Whenever there is a reference to realCall it should point to the symbol + // created for each wrap usage. + _wrapSymbolMap.insert(std::make_pair(realCallSym, wrapSymAtom)); + _undefinedAtoms._atoms.push_back(wrapSymAtom); + _undefinedAtoms._atoms.push_back(wrapCallAtom); + } + return std::error_code(); +} + +template <class ELFT> +ELFDefinedAtom<ELFT> *ELFFile<ELFT>::createDefinedAtomAndAssignRelocations( + StringRef symbolName, StringRef sectionName, const Elf_Sym *symbol, + const Elf_Shdr *section, ArrayRef<uint8_t> symContent, + ArrayRef<uint8_t> secContent) { + unsigned int referenceStart = _references.size(); + + // Add Rela (those with r_addend) references: + auto rari = _relocationAddendReferences.find(sectionName); + if (rari != _relocationAddendReferences.end()) + createRelocationReferences(symbol, symContent, rari->second); + + // Add Rel references. + auto rri = _relocationReferences.find(sectionName); + if (rri != _relocationReferences.end()) + createRelocationReferences(symbol, symContent, secContent, rri->second); + + // Create the DefinedAtom and add it to the list of DefinedAtoms. + return *handleDefinedSymbol(symbolName, sectionName, symbol, section, + symContent, referenceStart, _references.size(), + _references); +} + +template <class ELFT> +void ELFFile<ELFT>::createRelocationReferences(const Elf_Sym *symbol, + ArrayRef<uint8_t> content, + range<Elf_Rela_Iter> rels) { + bool isMips64EL = _objFile->isMips64EL(); + const auto symValue = getSymbolValue(symbol); + for (const auto &rel : rels) { + if (rel.r_offset < symValue || + symValue + content.size() <= rel.r_offset) + continue; + auto elfRelocation = new (_readerStorage) + ELFReference<ELFT>(&rel, rel.r_offset - symValue, kindArch(), + rel.getType(isMips64EL), rel.getSymbol(isMips64EL)); + addReferenceToSymbol(elfRelocation, symbol); + _references.push_back(elfRelocation); + } +} + +template <class ELFT> +void ELFFile<ELFT>::createRelocationReferences(const Elf_Sym *symbol, + ArrayRef<uint8_t> symContent, + ArrayRef<uint8_t> secContent, + range<Elf_Rel_Iter> rels) { + bool isMips64EL = _objFile->isMips64EL(); + const auto symValue = getSymbolValue(symbol); + for (const auto &rel : rels) { + if (rel.r_offset < symValue || + symValue + symContent.size() <= rel.r_offset) + continue; + auto elfRelocation = new (_readerStorage) + ELFReference<ELFT>(rel.r_offset - symValue, kindArch(), + rel.getType(isMips64EL), rel.getSymbol(isMips64EL)); + int32_t addend = *(symContent.data() + rel.r_offset - symValue); + elfRelocation->setAddend(addend); + addReferenceToSymbol(elfRelocation, symbol); + _references.push_back(elfRelocation); + } +} + +template <class ELFT> +void ELFFile<ELFT>::updateReferenceForMergeStringAccess(ELFReference<ELFT> *ref, + const Elf_Sym *symbol, + const Elf_Shdr *shdr) { + // If the target atom is mergeable strefng atom, the atom might have been + // merged with other atom having the same contents. Try to find the + // merged one if that's the case. + int64_t addend = ref->addend(); + if (addend < 0) + addend = 0; + + const MergeSectionKey ms(shdr, addend); + auto msec = _mergedSectionMap.find(ms); + if (msec != _mergedSectionMap.end()) { + ref->setTarget(msec->second); + return; + } + + // The target atom was not merged. Mergeable atoms are not in + // _symbolToAtomMapping, so we cannot find it by calling findAtom(). We + // instead call findMergeAtom(). + if (symbol->getType() != llvm::ELF::STT_SECTION) + addend = getSymbolValue(symbol) + addend; + ELFMergeAtom<ELFT> *mergedAtom = findMergeAtom(shdr, addend); + ref->setOffset(addend - mergedAtom->offset()); + ref->setAddend(0); + ref->setTarget(mergedAtom); +} + +template <class ELFT> void ELFFile<ELFT>::updateReferences() { + for (auto &ri : _references) { + if (ri->kindNamespace() != lld::Reference::KindNamespace::ELF) + continue; + const Elf_Sym *symbol = _objFile->getSymbol(ri->targetSymbolIndex()); + const Elf_Shdr *shdr = _objFile->getSection(symbol); + + // If the atom is not in mergeable string section, the target atom is + // simply that atom. + if (isMergeableStringSection(shdr)) + updateReferenceForMergeStringAccess(ri, symbol, shdr); + else + ri->setTarget(findAtom(findSymbolForReference(ri), symbol)); + } +} + +template <class ELFT> +bool ELFFile<ELFT>::isIgnoredSection(const Elf_Shdr *section) { + switch (section->sh_type) { + case llvm::ELF::SHT_NULL: + case llvm::ELF::SHT_STRTAB: + case llvm::ELF::SHT_SYMTAB: + case llvm::ELF::SHT_SYMTAB_SHNDX: + return true; + default: + break; + } + return false; +} + +template <class ELFT> +bool ELFFile<ELFT>::isMergeableStringSection(const Elf_Shdr *section) { + if (_doStringsMerge && section) { + int64_t sectionFlags = section->sh_flags; + sectionFlags &= ~llvm::ELF::SHF_ALLOC; + // Mergeable string sections have both SHF_MERGE and SHF_STRINGS flags + // set. sh_entsize is the size of each character which is normally 1. + if ((section->sh_entsize < 2) && + (sectionFlags == (llvm::ELF::SHF_MERGE | llvm::ELF::SHF_STRINGS))) { + return true; + } + } + return false; +} + +template <class ELFT> +ELFDefinedAtom<ELFT> * +ELFFile<ELFT>::createSectionAtom(const Elf_Shdr *section, StringRef sectionName, + ArrayRef<uint8_t> content) { + Elf_Sym *sym = new (_readerStorage) Elf_Sym; + sym->st_name = 0; + sym->setBindingAndType(llvm::ELF::STB_LOCAL, llvm::ELF::STT_SECTION); + sym->st_other = 0; + sym->st_shndx = 0; + sym->st_value = 0; + sym->st_size = 0; + auto *newAtom = createDefinedAtomAndAssignRelocations( + "", sectionName, sym, section, content, content); + newAtom->setOrdinal(++_ordinal); + return newAtom; +} + +template <class ELFT> +uint64_t ELFFile<ELFT>::symbolContentSize(const Elf_Shdr *section, + const Elf_Sym *symbol, + const Elf_Sym *nextSymbol) { + const auto symValue = getSymbolValue(symbol); + // if this is the last symbol, take up the remaining data. + return nextSymbol ? getSymbolValue(nextSymbol) - symValue + : section->sh_size - symValue; +} + +template <class ELFT> +void ELFFile<ELFT>::createEdge(ELFDefinedAtom<ELFT> *from, + ELFDefinedAtom<ELFT> *to, uint32_t edgeKind) { + auto reference = new (_readerStorage) ELFReference<ELFT>(edgeKind); + reference->setTarget(to); + from->addReference(reference); +} + +/// Does the atom need to be redirected using a separate undefined atom? +template <class ELFT> +bool ELFFile<ELFT>::redirectReferenceUsingUndefAtom( + const Elf_Sym *sourceSymbol, const Elf_Sym *targetSymbol) const { + auto groupChildTarget = _groupChild.find(targetSymbol); + + // If the reference is not to a group child atom, there is no need to redirect + // using a undefined atom. Its also not needed if the source and target are + // from the same section. + if ((groupChildTarget == _groupChild.end()) || + (sourceSymbol->st_shndx == targetSymbol->st_shndx)) + return false; + + auto groupChildSource = _groupChild.find(sourceSymbol); + + // If the source symbol is not in a group, use a undefined symbol too. + if (groupChildSource == _groupChild.end()) + return true; + + // If the source and child are from the same group, we dont need the + // relocation to go through a undefined symbol. + if (groupChildSource->second.second == groupChildTarget->second.second) + return false; + + return true; +} + +} // end namespace elf +} // end namespace lld + +#endif // LLD_READER_WRITER_ELF_FILE_H diff --git a/lib/ReaderWriter/ELF/ELFLinkingContext.cpp b/lib/ReaderWriter/ELF/ELFLinkingContext.cpp new file mode 100644 index 0000000000000..c7dffda8a463e --- /dev/null +++ b/lib/ReaderWriter/ELF/ELFLinkingContext.cpp @@ -0,0 +1,259 @@ +//===- lib/ReaderWriter/ELF/ELFLinkingContext.cpp -------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "ELFFile.h" +#include "OrderPass.h" +#include "TargetHandler.h" +#include "lld/Core/Instrumentation.h" +#include "lld/Core/SharedLibraryFile.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Config/config.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + +#if defined(HAVE_CXXABI_H) +#include <cxxabi.h> +#endif + +namespace lld { + +class CommandLineUndefinedAtom : public SimpleUndefinedAtom { +public: + CommandLineUndefinedAtom(const File &f, StringRef name) + : SimpleUndefinedAtom(f, name) {} + + CanBeNull canBeNull() const override { + return CanBeNull::canBeNullAtBuildtime; + } +}; + +ELFLinkingContext::ELFLinkingContext( + llvm::Triple triple, std::unique_ptr<TargetHandlerBase> targetHandler) + : _outputELFType(llvm::ELF::ET_EXEC), _triple(triple), + _targetHandler(std::move(targetHandler)), _baseAddress(0), + _isStaticExecutable(false), _noInhibitExec(false), _exportDynamic(false), + _mergeCommonStrings(false), _useShlibUndefines(true), + _dynamicLinkerArg(false), _noAllowDynamicLibraries(false), + _mergeRODataToTextSegment(true), _demangle(true), + _stripSymbols(false), _alignSegments(true), _collectStats(false), + _outputMagic(OutputMagic::DEFAULT), _initFunction("_init"), + _finiFunction("_fini"), _sysrootPath(""), _linkerScriptSema() {} + +void ELFLinkingContext::addPasses(PassManager &pm) { + pm.add(llvm::make_unique<elf::OrderPass>()); +} + +uint16_t ELFLinkingContext::getOutputMachine() const { + switch (getTriple().getArch()) { + case llvm::Triple::x86: + return llvm::ELF::EM_386; + case llvm::Triple::x86_64: + return llvm::ELF::EM_X86_64; + case llvm::Triple::hexagon: + return llvm::ELF::EM_HEXAGON; + case llvm::Triple::mipsel: + case llvm::Triple::mips64el: + return llvm::ELF::EM_MIPS; + case llvm::Triple::aarch64: + return llvm::ELF::EM_AARCH64; + case llvm::Triple::arm: + return llvm::ELF::EM_ARM; + default: + llvm_unreachable("Unhandled arch"); + } +} + +StringRef ELFLinkingContext::entrySymbolName() const { + if (_outputELFType == llvm::ELF::ET_EXEC && _entrySymbolName.empty()) + return "_start"; + return _entrySymbolName; +} + +bool ELFLinkingContext::validateImpl(raw_ostream &diagnostics) { + switch (outputFileType()) { + case LinkingContext::OutputFileType::YAML: + _writer = createWriterYAML(*this); + break; + case LinkingContext::OutputFileType::Native: + llvm_unreachable("Unimplemented"); + break; + default: + _writer = createWriterELF(this->targetHandler()); + break; + } + + // If -dead_strip, set up initial live symbols. + if (deadStrip()) + addDeadStripRoot(entrySymbolName()); + return true; +} + +bool ELFLinkingContext::isDynamic() const { + switch (_outputELFType) { + case llvm::ELF::ET_EXEC: + return !_isStaticExecutable; + case llvm::ELF::ET_DYN: + return true; + } + return false; +} + +bool ELFLinkingContext::isRelativeReloc(const Reference &) const { + return false; +} + +Writer &ELFLinkingContext::writer() const { return *_writer; } + +static void buildSearchPath(SmallString<128> &path, StringRef dir, + StringRef sysRoot) { + if (!dir.startswith("=/")) + path.assign(dir); + else { + path.assign(sysRoot); + path.append(dir.substr(1)); + } +} + +ErrorOr<StringRef> ELFLinkingContext::searchLibrary(StringRef libName) const { + bool hasColonPrefix = libName[0] == ':'; + SmallString<128> path; + for (StringRef dir : _inputSearchPaths) { + // Search for dynamic library + if (!_isStaticExecutable) { + buildSearchPath(path, dir, _sysrootPath); + llvm::sys::path::append(path, hasColonPrefix + ? libName.drop_front() + : Twine("lib", libName) + ".so"); + if (llvm::sys::fs::exists(path.str())) + return StringRef(*new (_allocator) std::string(path.str())); + } + // Search for static libraries too + buildSearchPath(path, dir, _sysrootPath); + llvm::sys::path::append(path, hasColonPrefix + ? libName.drop_front() + : Twine("lib", libName) + ".a"); + if (llvm::sys::fs::exists(path.str())) + return StringRef(*new (_allocator) std::string(path.str())); + } + if (hasColonPrefix && llvm::sys::fs::exists(libName.drop_front())) + return libName.drop_front(); + + return make_error_code(llvm::errc::no_such_file_or_directory); +} + +ErrorOr<StringRef> ELFLinkingContext::searchFile(StringRef fileName, + bool isSysRooted) const { + SmallString<128> path; + if (llvm::sys::path::is_absolute(fileName) && isSysRooted) { + path.assign(_sysrootPath); + path.append(fileName); + if (llvm::sys::fs::exists(path.str())) + return StringRef(*new (_allocator) std::string(path.str())); + } else if (llvm::sys::fs::exists(fileName)) + return fileName; + + if (llvm::sys::path::is_absolute(fileName)) + return make_error_code(llvm::errc::no_such_file_or_directory); + + for (StringRef dir : _inputSearchPaths) { + buildSearchPath(path, dir, _sysrootPath); + llvm::sys::path::append(path, fileName); + if (llvm::sys::fs::exists(path.str())) + return StringRef(*new (_allocator) std::string(path.str())); + } + return make_error_code(llvm::errc::no_such_file_or_directory); +} + +void ELFLinkingContext::createInternalFiles( + std::vector<std::unique_ptr<File>> &files) const { + std::unique_ptr<SimpleFile> file( + new SimpleFile("<internal file for --defsym>")); + for (auto &i : getAbsoluteSymbols()) { + StringRef sym = i.first; + uint64_t val = i.second; + file->addAtom(*(new (_allocator) SimpleAbsoluteAtom( + *file, sym, Atom::scopeGlobal, val))); + } + files.push_back(std::move(file)); + LinkingContext::createInternalFiles(files); +} + +void ELFLinkingContext::finalizeInputFiles() { + // Add virtual archive that resolves undefined symbols. + if (_resolver) + getNodes().push_back(llvm::make_unique<FileNode>(std::move(_resolver))); +} + +std::unique_ptr<File> ELFLinkingContext::createUndefinedSymbolFile() const { + if (_initialUndefinedSymbols.empty()) + return nullptr; + std::unique_ptr<SimpleFile> undefinedSymFile( + new SimpleFile("command line option -u")); + for (auto undefSymStr : _initialUndefinedSymbols) + undefinedSymFile->addAtom(*(new (_allocator) CommandLineUndefinedAtom( + *undefinedSymFile, undefSymStr))); + return std::move(undefinedSymFile); +} + +void ELFLinkingContext::notifySymbolTableCoalesce(const Atom *existingAtom, + const Atom *newAtom, + bool &useNew) { + // First suppose that the `existingAtom` is defined + // and the `newAtom` is undefined. + auto *da = dyn_cast<DefinedAtom>(existingAtom); + auto *ua = dyn_cast<UndefinedAtom>(newAtom); + if (!da && !ua) { + // Then try to reverse the assumption. + da = dyn_cast<DefinedAtom>(newAtom); + ua = dyn_cast<UndefinedAtom>(existingAtom); + } + + if (da && ua && da->scope() == Atom::scopeGlobal && + isa<SharedLibraryFile>(ua->file())) + // If strong defined atom coalesces away an atom declared + // in the shared object the strong atom needs to be dynamically exported. + // Save its name. + _dynamicallyExportedSymbols.insert(ua->name()); +} + +std::string ELFLinkingContext::demangle(StringRef symbolName) const { + if (!demangleSymbols()) + return symbolName; + + // Only try to demangle symbols that look like C++ symbols + if (!symbolName.startswith("_Z")) + return symbolName; + +#if defined(HAVE_CXXABI_H) + SmallString<256> symBuff; + StringRef nullTermSym = Twine(symbolName).toNullTerminatedStringRef(symBuff); + const char *cstr = nullTermSym.data(); + int status; + char *demangled = abi::__cxa_demangle(cstr, nullptr, nullptr, &status); + if (demangled != NULL) { + std::string result(demangled); + // __cxa_demangle() always uses a malloc'ed buffer to return the result. + free(demangled); + return result; + } +#endif + + return symbolName; +} + +void ELFLinkingContext::setUndefinesResolver(std::unique_ptr<File> resolver) { + assert(isa<ArchiveLibraryFile>(resolver.get()) && "Wrong resolver type"); + _resolver = std::move(resolver); +} + +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/ELFReader.h b/lib/ReaderWriter/ELF/ELFReader.h new file mode 100644 index 0000000000000..43f218115c666 --- /dev/null +++ b/lib/ReaderWriter/ELF/ELFReader.h @@ -0,0 +1,102 @@ +//===- lib/ReaderWriter/ELF/ELFReader.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_READER_H +#define LLD_READER_WRITER_ELF_READER_H + +#include "CreateELF.h" +#include "DynamicFile.h" +#include "ELFFile.h" +#include "lld/Core/Reader.h" + +namespace lld { +namespace elf { + +template <typename ELFT, typename ELFTraitsT, typename ContextT> +class ELFObjectReader : public Reader { +public: + typedef llvm::object::Elf_Ehdr_Impl<ELFT> Elf_Ehdr; + + ELFObjectReader(ContextT &ctx, uint64_t machine) + : _ctx(ctx), _machine(machine) {} + + bool canParse(file_magic magic, StringRef, + const MemoryBuffer &buf) const override { + return (magic == llvm::sys::fs::file_magic::elf_relocatable && + elfHeader(buf)->e_machine == _machine); + } + + std::error_code + loadFile(std::unique_ptr<MemoryBuffer> mb, const class Registry &, + std::vector<std::unique_ptr<File>> &result) const override { + std::size_t maxAlignment = + 1ULL << llvm::countTrailingZeros(uintptr_t(mb->getBufferStart())); + auto f = + createELF<ELFTraitsT>(llvm::object::getElfArchType(mb->getBuffer()), + maxAlignment, std::move(mb), _ctx); + if (std::error_code ec = f.getError()) + return ec; + result.push_back(std::move(*f)); + return std::error_code(); + } + + const Elf_Ehdr *elfHeader(const MemoryBuffer &buf) const { + const uint8_t *data = + reinterpret_cast<const uint8_t *>(buf.getBuffer().data()); + return (reinterpret_cast<const Elf_Ehdr *>(data)); + } + +protected: + ContextT &_ctx; + uint64_t _machine; +}; + +template <typename ELFT, typename ELFTraitsT, typename ContextT> +class ELFDSOReader : public Reader { +public: + typedef llvm::object::Elf_Ehdr_Impl<ELFT> Elf_Ehdr; + + ELFDSOReader(ContextT &ctx, uint64_t machine) + : _ctx(ctx), _machine(machine) {} + + bool canParse(file_magic magic, StringRef, + const MemoryBuffer &buf) const override { + return (magic == llvm::sys::fs::file_magic::elf_shared_object && + elfHeader(buf)->e_machine == _machine); + } + + std::error_code + loadFile(std::unique_ptr<MemoryBuffer> mb, const class Registry &, + std::vector<std::unique_ptr<File>> &result) const override { + std::size_t maxAlignment = + 1ULL << llvm::countTrailingZeros(uintptr_t(mb->getBufferStart())); + auto f = + createELF<ELFTraitsT>(llvm::object::getElfArchType(mb->getBuffer()), + maxAlignment, std::move(mb), _ctx); + if (std::error_code ec = f.getError()) + return ec; + result.push_back(std::move(*f)); + return std::error_code(); + } + + const Elf_Ehdr *elfHeader(const MemoryBuffer &buf) const { + const uint8_t *data = + reinterpret_cast<const uint8_t *>(buf.getBuffer().data()); + return (reinterpret_cast<const Elf_Ehdr *>(data)); + } + +protected: + ContextT &_ctx; + uint64_t _machine; +}; + +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_ELF_READER_H diff --git a/lib/ReaderWriter/ELF/ExecutableWriter.h b/lib/ReaderWriter/ELF/ExecutableWriter.h new file mode 100644 index 0000000000000..477e3920abaee --- /dev/null +++ b/lib/ReaderWriter/ELF/ExecutableWriter.h @@ -0,0 +1,182 @@ +//===- lib/ReaderWriter/ELF/ExecutableWriter.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_EXECUTABLE_WRITER_H +#define LLD_READER_WRITER_ELF_EXECUTABLE_WRITER_H + +#include "OutputELFWriter.h" + +namespace lld { +namespace elf { +using namespace llvm; +using namespace llvm::object; + +template<class ELFT> +class ExecutableWriter; + +//===----------------------------------------------------------------------===// +// ExecutableWriter Class +//===----------------------------------------------------------------------===// +template<class ELFT> +class ExecutableWriter : public OutputELFWriter<ELFT> { +public: + ExecutableWriter(ELFLinkingContext &context, TargetLayout<ELFT> &layout) + : OutputELFWriter<ELFT>(context, layout), + _runtimeFile(new RuntimeFile<ELFT>(context, "C runtime")) {} + +protected: + virtual void buildDynamicSymbolTable(const File &file); + virtual void addDefaultAtoms(); + virtual bool createImplicitFiles(std::vector<std::unique_ptr<File> > &); + virtual void finalizeDefaultAtomValues(); + virtual void createDefaultSections(); + + virtual bool isNeededTagRequired(const SharedLibraryAtom *sla) const { + return this->_layout.isCopied(sla); + } + + unique_bump_ptr<InterpSection<ELFT>> _interpSection; + std::unique_ptr<RuntimeFile<ELFT> > _runtimeFile; +}; + +//===----------------------------------------------------------------------===// +// ExecutableWriter +//===----------------------------------------------------------------------===// +template<class ELFT> +void ExecutableWriter<ELFT>::buildDynamicSymbolTable(const File &file) { + for (auto sec : this->_layout.sections()) + if (auto section = dyn_cast<AtomSection<ELFT>>(sec)) + for (const auto &atom : section->atoms()) { + const DefinedAtom *da = dyn_cast<const DefinedAtom>(atom->_atom); + if (!da) + continue; + if (da->dynamicExport() != DefinedAtom::dynamicExportAlways && + !this->_context.isDynamicallyExportedSymbol(da->name()) && + !(this->_context.shouldExportDynamic() && + da->scope() == Atom::Scope::scopeGlobal)) + continue; + this->_dynamicSymbolTable->addSymbol(atom->_atom, section->ordinal(), + atom->_virtualAddr, atom); + } + + // Put weak symbols in the dynamic symbol table. + if (this->_context.isDynamic()) { + for (const UndefinedAtom *a : file.undefined()) { + if (this->_layout.isReferencedByDefinedAtom(a) && + a->canBeNull() != UndefinedAtom::canBeNullNever) + this->_dynamicSymbolTable->addSymbol(a, ELF::SHN_UNDEF); + } + } + + OutputELFWriter<ELFT>::buildDynamicSymbolTable(file); +} + +/// \brief Add absolute symbols by default. These are linker added +/// absolute symbols +template<class ELFT> +void ExecutableWriter<ELFT>::addDefaultAtoms() { + OutputELFWriter<ELFT>::addDefaultAtoms(); + _runtimeFile->addUndefinedAtom(this->_context.entrySymbolName()); + _runtimeFile->addAbsoluteAtom("__bss_start"); + _runtimeFile->addAbsoluteAtom("__bss_end"); + _runtimeFile->addAbsoluteAtom("_end"); + _runtimeFile->addAbsoluteAtom("end"); + _runtimeFile->addAbsoluteAtom("__preinit_array_start"); + _runtimeFile->addAbsoluteAtom("__preinit_array_end"); + _runtimeFile->addAbsoluteAtom("__init_array_start"); + _runtimeFile->addAbsoluteAtom("__init_array_end"); + if (this->_context.isRelaOutputFormat()) { + _runtimeFile->addAbsoluteAtom("__rela_iplt_start"); + _runtimeFile->addAbsoluteAtom("__rela_iplt_end"); + } else { + _runtimeFile->addAbsoluteAtom("__rel_iplt_start"); + _runtimeFile->addAbsoluteAtom("__rel_iplt_end"); + } + _runtimeFile->addAbsoluteAtom("__fini_array_start"); + _runtimeFile->addAbsoluteAtom("__fini_array_end"); +} + +/// \brief Hook in lld to add CRuntime file +template <class ELFT> +bool ExecutableWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File> > &result) { + // Add the default atoms as defined by executables + ExecutableWriter<ELFT>::addDefaultAtoms(); + OutputELFWriter<ELFT>::createImplicitFiles(result); + result.push_back(std::move(_runtimeFile)); + return true; +} + +template <class ELFT> void ExecutableWriter<ELFT>::createDefaultSections() { + OutputELFWriter<ELFT>::createDefaultSections(); + if (this->_context.isDynamic()) { + _interpSection.reset(new (this->_alloc) InterpSection<ELFT>( + this->_context, ".interp", DefaultLayout<ELFT>::ORDER_INTERP, + this->_context.getInterpreter())); + this->_layout.addSection(_interpSection.get()); + } +} + +/// Finalize the value of all the absolute symbols that we +/// created +template <class ELFT> void ExecutableWriter<ELFT>::finalizeDefaultAtomValues() { + OutputELFWriter<ELFT>::finalizeDefaultAtomValues(); + auto bssStartAtomIter = this->_layout.findAbsoluteAtom("__bss_start"); + auto bssEndAtomIter = this->_layout.findAbsoluteAtom("__bss_end"); + auto underScoreEndAtomIter = this->_layout.findAbsoluteAtom("_end"); + auto endAtomIter = this->_layout.findAbsoluteAtom("end"); + + auto startEnd = [&](StringRef sym, StringRef sec) -> void { + std::string start = ("__" + sym + "_start").str(); + std::string end = ("__" + sym + "_end").str(); + auto s = this->_layout.findAbsoluteAtom(start); + auto e = this->_layout.findAbsoluteAtom(end); + auto section = this->_layout.findOutputSection(sec); + if (section) { + (*s)->_virtualAddr = section->virtualAddr(); + (*e)->_virtualAddr = section->virtualAddr() + section->memSize(); + } else { + (*s)->_virtualAddr = 0; + (*e)->_virtualAddr = 0; + } + }; + + startEnd("preinit_array", ".preinit_array"); + startEnd("init_array", ".init_array"); + if (this->_context.isRelaOutputFormat()) + startEnd("rela_iplt", ".rela.plt"); + else + startEnd("rel_iplt", ".rel.plt"); + startEnd("fini_array", ".fini_array"); + + assert(!(bssStartAtomIter == this->_layout.absoluteAtoms().end() || + bssEndAtomIter == this->_layout.absoluteAtoms().end() || + underScoreEndAtomIter == this->_layout.absoluteAtoms().end() || + endAtomIter == this->_layout.absoluteAtoms().end()) && + "Unable to find the absolute atoms that have been added by lld"); + + auto bssSection = this->_layout.findOutputSection(".bss"); + + // If we don't find a bss section, then don't set these values + if (bssSection) { + (*bssStartAtomIter)->_virtualAddr = bssSection->virtualAddr(); + (*bssEndAtomIter)->_virtualAddr = + bssSection->virtualAddr() + bssSection->memSize(); + (*underScoreEndAtomIter)->_virtualAddr = (*bssEndAtomIter)->_virtualAddr; + (*endAtomIter)->_virtualAddr = (*bssEndAtomIter)->_virtualAddr; + } else if (auto dataSection = this->_layout.findOutputSection(".data")) { + (*underScoreEndAtomIter)->_virtualAddr = + dataSection->virtualAddr() + dataSection->memSize(); + (*endAtomIter)->_virtualAddr = (*underScoreEndAtomIter)->_virtualAddr; + } +} + +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_ELF_EXECUTABLE_WRITER_H diff --git a/lib/ReaderWriter/ELF/HeaderChunks.h b/lib/ReaderWriter/ELF/HeaderChunks.h new file mode 100644 index 0000000000000..eab132b9b2f69 --- /dev/null +++ b/lib/ReaderWriter/ELF/HeaderChunks.h @@ -0,0 +1,364 @@ +//===- lib/ReaderWriter/ELF/HeaderChunks.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_HEADER_CHUNKS_H +#define LLD_READER_WRITER_ELF_HEADER_CHUNKS_H + +#include "SegmentChunks.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/Format.h" + +/// \brief An Header represents the Elf[32/64]_Ehdr structure at the +/// start of an ELF executable file. +namespace lld { +namespace elf { +template <class ELFT> class ELFHeader : public Chunk<ELFT> { +public: + typedef llvm::object::Elf_Ehdr_Impl<ELFT> Elf_Ehdr; + + ELFHeader(const ELFLinkingContext &); + + void e_ident(int I, unsigned char C) { _eh.e_ident[I] = C; } + void e_type(uint16_t type) { _eh.e_type = type; } + void e_machine(uint16_t machine) { _eh.e_machine = machine; } + void e_version(uint32_t version) { _eh.e_version = version; } + void e_entry(int64_t entry) { _eh.e_entry = entry; } + void e_phoff(int64_t phoff) { _eh.e_phoff = phoff; } + void e_shoff(int64_t shoff) { _eh.e_shoff = shoff; } + void e_flags(uint32_t flags) { _eh.e_flags = flags; } + void e_ehsize(uint16_t ehsize) { _eh.e_ehsize = ehsize; } + void e_phentsize(uint16_t phentsize) { _eh.e_phentsize = phentsize; } + void e_phnum(uint16_t phnum) { _eh.e_phnum = phnum; } + void e_shentsize(uint16_t shentsize) { _eh.e_shentsize = shentsize; } + void e_shnum(uint16_t shnum) { _eh.e_shnum = shnum; } + void e_shstrndx(uint16_t shstrndx) { _eh.e_shstrndx = shstrndx; } + uint64_t fileSize() const { return sizeof(Elf_Ehdr); } + + static bool classof(const Chunk<ELFT> *c) { + return c->Kind() == Chunk<ELFT>::Kind::ELFHeader; + } + + int getContentType() const { return Chunk<ELFT>::ContentType::Header; } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer); + + virtual void doPreFlight() {} + + void finalize() { + _eh.e_ident[llvm::ELF::EI_CLASS] = + (ELFT::Is64Bits) ? llvm::ELF::ELFCLASS64 : llvm::ELF::ELFCLASS32; + _eh.e_ident[llvm::ELF::EI_DATA] = + (ELFT::TargetEndianness == llvm::support::little) + ? llvm::ELF::ELFDATA2LSB + : llvm::ELF::ELFDATA2MSB; + _eh.e_type = this->_context.getOutputELFType(); + _eh.e_machine = this->_context.getOutputMachine(); + } + +private: + Elf_Ehdr _eh; +}; + +template <class ELFT> +ELFHeader<ELFT>::ELFHeader(const ELFLinkingContext &context) + : Chunk<ELFT>("elfhdr", Chunk<ELFT>::Kind::ELFHeader, context) { + this->_alignment = ELFT::Is64Bits ? 8 : 4; + this->_fsize = sizeof(Elf_Ehdr); + this->_msize = sizeof(Elf_Ehdr); + memset(_eh.e_ident, 0, llvm::ELF::EI_NIDENT); + e_ident(llvm::ELF::EI_MAG0, 0x7f); + e_ident(llvm::ELF::EI_MAG1, 'E'); + e_ident(llvm::ELF::EI_MAG2, 'L'); + e_ident(llvm::ELF::EI_MAG3, 'F'); + e_ehsize(sizeof(Elf_Ehdr)); + e_flags(0); +} + +template <class ELFT> +void ELFHeader<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *atomContent = chunkBuffer + this->fileOffset(); + memcpy(atomContent, &_eh, fileSize()); +} + +/// \brief An ProgramHeader represents the Elf[32/64]_Phdr structure at the +/// start of an ELF executable file. +template<class ELFT> +class ProgramHeader : public Chunk<ELFT> { +public: + typedef llvm::object::Elf_Phdr_Impl<ELFT> Elf_Phdr; + typedef typename std::vector<Elf_Phdr *>::iterator PhIterT; + typedef typename std::reverse_iterator<PhIterT> ReversePhIterT; + + /// \brief Find a program header entry, given the type of entry that + /// we are looking for + class FindPhdr { + public: + FindPhdr(uint64_t type, uint64_t flags, uint64_t flagsClear) + : _type(type) + , _flags(flags) + , _flagsClear(flagsClear) { + } + + bool operator()(const llvm::object::Elf_Phdr_Impl<ELFT> *j) const { + return ((j->p_type == _type) && + ((j->p_flags & _flags) == _flags) && + (!(j->p_flags & _flagsClear))); + } + private: + uint64_t _type; + uint64_t _flags; + uint64_t _flagsClear; + }; + + ProgramHeader(const ELFLinkingContext &context) + : Chunk<ELFT>("elfphdr", Chunk<ELFT>::Kind::ProgramHeader, context) { + this->_alignment = ELFT::Is64Bits ? 8 : 4; + resetProgramHeaders(); + } + + bool addSegment(Segment<ELFT> *segment); + + void resetProgramHeaders() { _phi = _ph.begin(); } + + uint64_t fileSize() const { return sizeof(Elf_Phdr) * _ph.size(); } + + static bool classof(const Chunk<ELFT> *c) { + return c->Kind() == Chunk<ELFT>::Kind::ProgramHeader; + } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer); + + /// \brief find a program header entry in the list of program headers + ReversePhIterT + findProgramHeader(uint64_t type, uint64_t flags, uint64_t flagClear) { + return std::find_if(_ph.rbegin(), _ph.rend(), + FindPhdr(type, flags, flagClear)); + } + + PhIterT begin() { + return _ph.begin(); + } + + PhIterT end() { + return _ph.end(); + } + + ReversePhIterT rbegin() { return _ph.rbegin(); } + + ReversePhIterT rend() { return _ph.rend(); } + + virtual void doPreFlight() {} + + void finalize() {} + + int64_t entsize() { return sizeof(Elf_Phdr); } + + int64_t numHeaders() { + return _ph.size(); + } + + int getContentType() const { return Chunk<ELFT>::ContentType::Header; } + +private: + Elf_Phdr *allocateProgramHeader(bool &allocatedNew) { + Elf_Phdr *phdr; + if (_phi == _ph.end()) { + phdr = new (_allocator) Elf_Phdr; + _ph.push_back(phdr); + _phi = _ph.end(); + allocatedNew = true; + } else { + phdr = (*_phi); + ++_phi; + } + return phdr; + } + + std::vector<Elf_Phdr *> _ph; + PhIterT _phi; + llvm::BumpPtrAllocator _allocator; +}; + +template <class ELFT> +bool ProgramHeader<ELFT>::addSegment(Segment<ELFT> *segment) { + bool allocatedNew = false; + ELFLinkingContext::OutputMagic outputMagic = this->_context.getOutputMagic(); + // For segments that are not a loadable segment, we + // just pick the values directly from the segment as there + // wouldnt be any slices within that + if (segment->segmentType() != llvm::ELF::PT_LOAD) { + Elf_Phdr *phdr = allocateProgramHeader(allocatedNew); + phdr->p_type = segment->segmentType(); + phdr->p_offset = segment->fileOffset(); + phdr->p_vaddr = segment->virtualAddr(); + phdr->p_paddr = segment->virtualAddr(); + phdr->p_filesz = segment->fileSize(); + phdr->p_memsz = segment->memSize(); + phdr->p_flags = segment->flags(); + phdr->p_align = segment->alignment(); + this->_fsize = fileSize(); + this->_msize = this->_fsize; + return allocatedNew; + } + // For all other segments, use the slice + // to derive program headers + for (auto slice : segment->slices()) { + Elf_Phdr *phdr = allocateProgramHeader(allocatedNew); + phdr->p_type = segment->segmentType(); + phdr->p_offset = slice->fileOffset(); + phdr->p_vaddr = slice->virtualAddr(); + phdr->p_paddr = slice->virtualAddr(); + phdr->p_filesz = slice->fileSize(); + phdr->p_memsz = slice->memSize(); + phdr->p_flags = segment->flags(); + phdr->p_align = slice->alignment(); + uint64_t segPageSize = segment->pageSize(); + uint64_t sliceAlign = slice->alignment(); + // Alignment of PT_LOAD segments are set to the page size, but if the + // alignment of the slice is greater than the page size, set the alignment + // of the segment appropriately. + if (outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && + outputMagic != ELFLinkingContext::OutputMagic::OMAGIC) { + phdr->p_align = (phdr->p_type == llvm::ELF::PT_LOAD) + ? (segPageSize < sliceAlign) ? sliceAlign : segPageSize + : sliceAlign; + } else + phdr->p_align = slice->alignment(); + } + this->_fsize = fileSize(); + this->_msize = this->_fsize; + + return allocatedNew; +} + +template <class ELFT> +void ProgramHeader<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + for (auto phi : _ph) { + memcpy(dest, phi, sizeof(Elf_Phdr)); + dest += sizeof(Elf_Phdr); + } +} + +/// \brief An SectionHeader represents the Elf[32/64]_Shdr structure +/// at the end of the file +template<class ELFT> +class SectionHeader : public Chunk<ELFT> { +public: + typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + + SectionHeader(const ELFLinkingContext &, int32_t order); + + void appendSection(OutputSection<ELFT> *section); + + void updateSection(Section<ELFT> *section); + + static bool classof(const Chunk<ELFT> *c) { + return c->getChunkKind() == Chunk<ELFT>::Kind::SectionHeader; + } + + void setStringSection(StringTable<ELFT> *s) { + _stringSection = s; + } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer); + + virtual void doPreFlight() {} + + void finalize() {} + + uint64_t fileSize() const { return sizeof(Elf_Shdr) * _sectionInfo.size(); } + + uint64_t entsize() { return sizeof(Elf_Shdr); } + + int getContentType() const { return Chunk<ELFT>::ContentType::Header; } + + uint64_t numHeaders() { return _sectionInfo.size(); } + +private: + StringTable<ELFT> *_stringSection; + std::vector<Elf_Shdr*> _sectionInfo; + llvm::BumpPtrAllocator _sectionAllocate; +}; + +template <class ELFT> +SectionHeader<ELFT>::SectionHeader(const ELFLinkingContext &context, + int32_t order) + : Chunk<ELFT>("shdr", Chunk<ELFT>::Kind::SectionHeader, context) { + this->_fsize = 0; + this->_alignment = 8; + this->setOrder(order); + // The first element in the list is always NULL + Elf_Shdr *nullshdr = new (_sectionAllocate.Allocate<Elf_Shdr>()) Elf_Shdr; + ::memset(nullshdr, 0, sizeof (Elf_Shdr)); + _sectionInfo.push_back(nullshdr); + this->_fsize += sizeof (Elf_Shdr); +} + +template <class ELFT> +void SectionHeader<ELFT>::appendSection(OutputSection<ELFT> *section) { + Elf_Shdr *shdr = new (_sectionAllocate.Allocate<Elf_Shdr>()) Elf_Shdr; + shdr->sh_name = _stringSection->addString(section->name()); + shdr->sh_type = section->type(); + shdr->sh_flags = section->flags(); + shdr->sh_offset = section->fileOffset(); + shdr->sh_addr = section->virtualAddr(); + if (section->isLoadableSection()) + shdr->sh_size = section->memSize(); + else + shdr->sh_size = section->fileSize(); + shdr->sh_link = section->link(); + shdr->sh_info = section->shinfo(); + shdr->sh_addralign = section->alignment(); + shdr->sh_entsize = section->entsize(); + _sectionInfo.push_back(shdr); +} + +template<class ELFT> +void +SectionHeader<ELFT>::updateSection(Section<ELFT> *section) { + Elf_Shdr *shdr = _sectionInfo[section->ordinal()]; + shdr->sh_type = section->getType(); + shdr->sh_flags = section->getFlags(); + shdr->sh_offset = section->fileOffset(); + shdr->sh_addr = section->virtualAddr(); + shdr->sh_size = section->fileSize(); + shdr->sh_link = section->getLink(); + shdr->sh_info = section->getInfo(); + shdr->sh_addralign = section->alignment(); + shdr->sh_entsize = section->getEntSize(); +} + +template <class ELFT> +void SectionHeader<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + for (auto shi : _sectionInfo) { + memcpy(dest, shi, sizeof(Elf_Shdr)); + dest += sizeof(Elf_Shdr); + } + _stringSection->write(writer, layout, buffer); +} +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Hexagon/CMakeLists.txt b/lib/ReaderWriter/ELF/Hexagon/CMakeLists.txt new file mode 100644 index 0000000000000..6928f43c54592 --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/CMakeLists.txt @@ -0,0 +1,11 @@ +add_llvm_library(lldHexagonELFTarget + HexagonLinkingContext.cpp + HexagonRelocationHandler.cpp + HexagonTargetHandler.cpp + LINK_LIBS + lldELF + lldReaderWriter + lldCore + LLVMObject + LLVMSupport + ) diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonDynamicLibraryWriter.h b/lib/ReaderWriter/ELF/Hexagon/HexagonDynamicLibraryWriter.h new file mode 100644 index 0000000000000..e2d3193045b75 --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonDynamicLibraryWriter.h @@ -0,0 +1,79 @@ +//===- lib/ReaderWriter/ELF/Hexagon/HexagonDynamicLibraryWriter.h ---------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef HEXAGON_DYNAMIC_LIBRARY_WRITER_H +#define HEXAGON_DYNAMIC_LIBRARY_WRITER_H + +#include "DynamicLibraryWriter.h" +#include "HexagonExecutableAtoms.h" +#include "HexagonLinkingContext.h" + +namespace lld { +namespace elf { + +template <typename ELFT> class HexagonTargetLayout; + +template <class ELFT> +class HexagonDynamicLibraryWriter : public DynamicLibraryWriter<ELFT>, + public HexagonELFWriter<ELFT> { +public: + HexagonDynamicLibraryWriter(HexagonLinkingContext &context, + HexagonTargetLayout<ELFT> &layout); + +protected: + // Add any runtime files and their atoms to the output + virtual bool createImplicitFiles(std::vector<std::unique_ptr<File>> &); + + virtual void finalizeDefaultAtomValues(); + + virtual std::error_code setELFHeader() { + DynamicLibraryWriter<ELFT>::setELFHeader(); + HexagonELFWriter<ELFT>::setELFHeader(*this->_elfHeader); + return std::error_code(); + } + +private: + void addDefaultAtoms() { + _hexagonRuntimeFile->addAbsoluteAtom("_GLOBAL_OFFSET_TABLE_"); + _hexagonRuntimeFile->addAbsoluteAtom("_DYNAMIC"); + } + + HexagonLinkingContext &_hexagonLinkingContext; + HexagonTargetLayout<ELFT> &_hexagonTargetLayout; + std::unique_ptr<HexagonRuntimeFile<ELFT>> _hexagonRuntimeFile; +}; + +template <class ELFT> +HexagonDynamicLibraryWriter<ELFT>::HexagonDynamicLibraryWriter( + HexagonLinkingContext &context, HexagonTargetLayout<ELFT> &layout) + : DynamicLibraryWriter<ELFT>(context, layout), + HexagonELFWriter<ELFT>(context, layout), _hexagonLinkingContext(context), + _hexagonTargetLayout(layout), + _hexagonRuntimeFile(new HexagonRuntimeFile<ELFT>(context)) {} + +template <class ELFT> +bool HexagonDynamicLibraryWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + DynamicLibraryWriter<ELFT>::createImplicitFiles(result); + // Add the default atoms as defined for hexagon + addDefaultAtoms(); + result.push_back(std::move(_hexagonRuntimeFile)); + return true; +} + +template <class ELFT> +void HexagonDynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues() { + // Finalize the atom values that are part of the parent. + DynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues(); + HexagonELFWriter<ELFT>::finalizeHexagonRuntimeAtomValues(); +} + +} // namespace elf +} // namespace lld + +#endif // HEXAGON_DYNAMIC_LIBRARY_WRITER_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonELFFile.h b/lib/ReaderWriter/ELF/Hexagon/HexagonELFFile.h new file mode 100644 index 0000000000000..ab0b9b432b430 --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonELFFile.h @@ -0,0 +1,170 @@ +//===- lib/ReaderWriter/ELF/HexagonELFFile.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_HEXAGON_ELF_FILE_H +#define LLD_READER_WRITER_ELF_HEXAGON_ELF_FILE_H + +#include "ELFReader.h" +#include "HexagonLinkingContext.h" + +namespace lld { +namespace elf { + +template <class ELFT> class HexagonELFFile; + +template <class ELFT> +class HexagonELFDefinedAtom : public ELFDefinedAtom<ELFT> { + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + +public: + HexagonELFDefinedAtom(const HexagonELFFile<ELFT> &file, StringRef symbolName, + StringRef sectionName, const Elf_Sym *symbol, + const Elf_Shdr *section, ArrayRef<uint8_t> contentData, + unsigned int referenceStart, unsigned int referenceEnd, + std::vector<ELFReference<ELFT> *> &referenceList) + : ELFDefinedAtom<ELFT>(file, symbolName, sectionName, symbol, section, + contentData, referenceStart, referenceEnd, + referenceList) {} + + virtual DefinedAtom::ContentType contentType() const { + if (this->_contentType != DefinedAtom::typeUnknown) + return this->_contentType; + else if (this->_section->sh_flags & llvm::ELF::SHF_HEX_GPREL) { + if (this->_section->sh_type == llvm::ELF::SHT_NOBITS) + return (this->_contentType = DefinedAtom::typeZeroFillFast); + else + return (this->_contentType = DefinedAtom::typeDataFast); + } + return ELFDefinedAtom<ELFT>::contentType(); + } + + virtual DefinedAtom::ContentPermissions permissions() const { + if (this->_section->sh_flags & llvm::ELF::SHF_HEX_GPREL) + return DefinedAtom::permRW_; + return ELFDefinedAtom<ELFT>::permissions(); + } +}; + +template <class ELFT> class HexagonELFCommonAtom : public ELFCommonAtom<ELFT> { + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + +public: + HexagonELFCommonAtom(const HexagonELFFile<ELFT> &file, StringRef symbolName, + const Elf_Sym *symbol) + : ELFCommonAtom<ELFT>(file, symbolName, symbol) {} + + virtual bool isSmallCommonSymbol() const { + switch (this->_symbol->st_shndx) { + // Common symbols + case llvm::ELF::SHN_HEXAGON_SCOMMON: + case llvm::ELF::SHN_HEXAGON_SCOMMON_1: + case llvm::ELF::SHN_HEXAGON_SCOMMON_2: + case llvm::ELF::SHN_HEXAGON_SCOMMON_4: + case llvm::ELF::SHN_HEXAGON_SCOMMON_8: + return true; + default: + break; + } + return false; + } + + virtual uint64_t size() const { + if (isSmallCommonSymbol()) + return this->_symbol->st_size; + return ELFCommonAtom<ELFT>::size(); + } + + virtual DefinedAtom::Merge merge() const { + if (this->_symbol->getBinding() == llvm::ELF::STB_WEAK) + return DefinedAtom::mergeAsWeak; + if (isSmallCommonSymbol()) + return DefinedAtom::mergeAsTentative; + return ELFCommonAtom<ELFT>::merge(); + } + + virtual DefinedAtom::ContentType contentType() const { + if (isSmallCommonSymbol()) + return DefinedAtom::typeZeroFillFast; + return ELFCommonAtom<ELFT>::contentType(); + } + + virtual DefinedAtom::Alignment alignment() const { + if (isSmallCommonSymbol()) + return DefinedAtom::Alignment(llvm::Log2_64(this->_symbol->st_value)); + return ELFCommonAtom<ELFT>::alignment(); + } + + virtual DefinedAtom::ContentPermissions permissions() const { + if (isSmallCommonSymbol()) + return DefinedAtom::permRW_; + return ELFCommonAtom<ELFT>::permissions(); + } +}; + +template <class ELFT> class HexagonELFFile : public ELFFile<ELFT> { + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + +public: + HexagonELFFile(std::unique_ptr<MemoryBuffer> mb, HexagonLinkingContext &ctx) + : ELFFile<ELFT>(std::move(mb), ctx) {} + + static ErrorOr<std::unique_ptr<HexagonELFFile>> + create(std::unique_ptr<MemoryBuffer> mb, HexagonLinkingContext &ctx) { + return std::unique_ptr<HexagonELFFile<ELFT>>( + new HexagonELFFile<ELFT>(std::move(mb), ctx)); + } + + bool isCommonSymbol(const Elf_Sym *symbol) const override { + switch (symbol->st_shndx) { + // Common symbols + case llvm::ELF::SHN_HEXAGON_SCOMMON: + case llvm::ELF::SHN_HEXAGON_SCOMMON_1: + case llvm::ELF::SHN_HEXAGON_SCOMMON_2: + case llvm::ELF::SHN_HEXAGON_SCOMMON_4: + case llvm::ELF::SHN_HEXAGON_SCOMMON_8: + return true; + default: + break; + } + return ELFFile<ELFT>::isCommonSymbol(symbol); + } + + /// Process the Defined symbol and create an atom for it. + ErrorOr<ELFDefinedAtom<ELFT> *> + handleDefinedSymbol(StringRef symName, StringRef sectionName, + const Elf_Sym *sym, const Elf_Shdr *sectionHdr, + ArrayRef<uint8_t> contentData, + unsigned int referenceStart, unsigned int referenceEnd, + std::vector<ELFReference<ELFT> *> &referenceList) override { + return new (this->_readerStorage) HexagonELFDefinedAtom<ELFT>( + *this, symName, sectionName, sym, sectionHdr, contentData, + referenceStart, referenceEnd, referenceList); + } + + /// Process the Common symbol and create an atom for it. + ErrorOr<ELFCommonAtom<ELFT> *> + handleCommonSymbol(StringRef symName, const Elf_Sym *sym) override { + return new (this->_readerStorage) + HexagonELFCommonAtom<ELFT>(*this, symName, sym); + } +}; + +template <class ELFT> class HexagonDynamicFile : public DynamicFile<ELFT> { +public: + HexagonDynamicFile(const HexagonLinkingContext &context, StringRef name) + : DynamicFile<ELFT>(context, name) {} +}; + +} // elf +} // lld + +#endif // LLD_READER_WRITER_ELF_HEXAGON_ELF_FILE_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonELFReader.h b/lib/ReaderWriter/ELF/Hexagon/HexagonELFReader.h new file mode 100644 index 0000000000000..1a4f891df7997 --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonELFReader.h @@ -0,0 +1,62 @@ +//===- lib/ReaderWriter/ELF/HexagonELFReader.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_HEXAGON_ELF_READER_H +#define LLD_READER_WRITER_HEXAGON_ELF_READER_H + +#include "ELFReader.h" +#include "HexagonELFFile.h" + +namespace lld { +namespace elf { + +typedef llvm::object::ELFType<llvm::support::little, 2, false> HexagonELFType; + +struct HexagonDynamicFileCreateELFTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::SharedLibraryFile>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + HexagonLinkingContext &ctx) { + return lld::elf::HexagonDynamicFile<ELFT>::create(std::move(mb), ctx); + } +}; + +struct HexagonELFFileCreateELFTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::File>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + HexagonLinkingContext &ctx) { + return lld::elf::HexagonELFFile<ELFT>::create(std::move(mb), ctx); + } +}; + +class HexagonELFObjectReader + : public ELFObjectReader<HexagonELFType, HexagonELFFileCreateELFTraits, + HexagonLinkingContext> { +public: + HexagonELFObjectReader(HexagonLinkingContext &ctx) + : ELFObjectReader<HexagonELFType, HexagonELFFileCreateELFTraits, + HexagonLinkingContext>(ctx, llvm::ELF::EM_HEXAGON) {} +}; + +class HexagonELFDSOReader + : public ELFDSOReader<HexagonELFType, HexagonDynamicFileCreateELFTraits, + HexagonLinkingContext> { +public: + HexagonELFDSOReader(HexagonLinkingContext &ctx) + : ELFDSOReader<HexagonELFType, HexagonDynamicFileCreateELFTraits, + HexagonLinkingContext>(ctx, llvm::ELF::EM_HEXAGON) {} +}; + +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_ELF_READER_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonELFWriters.h b/lib/ReaderWriter/ELF/Hexagon/HexagonELFWriters.h new file mode 100644 index 0000000000000..96c74f72222dc --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonELFWriters.h @@ -0,0 +1,61 @@ +//===- lib/ReaderWriter/ELF/Hexagon/HexagonELFWriters.h -------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef HEXAGON_ELF_WRITERS_H +#define HEXAGON_ELF_WRITERS_H + +#include "HexagonLinkingContext.h" +#include "OutputELFWriter.h" + +namespace lld { +namespace elf { + +template <class ELFT> class HexagonTargetLayout; + +template <typename ELFT> class HexagonELFWriter { +public: + HexagonELFWriter(HexagonLinkingContext &context, + HexagonTargetLayout<ELFT> &targetLayout) + : _hexagonLinkingContext(context), _hexagonTargetLayout(targetLayout) {} + +protected: + bool setELFHeader(ELFHeader<ELFT> &elfHeader) { + elfHeader.e_ident(llvm::ELF::EI_VERSION, 1); + elfHeader.e_ident(llvm::ELF::EI_OSABI, 0); + elfHeader.e_version(1); + elfHeader.e_flags(0x3); + return true; + } + + void finalizeHexagonRuntimeAtomValues() { + if (_hexagonLinkingContext.isDynamic()) { + auto gotAtomIter = + _hexagonTargetLayout.findAbsoluteAtom("_GLOBAL_OFFSET_TABLE_"); + auto gotpltSection = _hexagonTargetLayout.findOutputSection(".got.plt"); + if (gotpltSection) + (*gotAtomIter)->_virtualAddr = gotpltSection->virtualAddr(); + else + (*gotAtomIter)->_virtualAddr = 0; + auto dynamicAtomIter = _hexagonTargetLayout.findAbsoluteAtom("_DYNAMIC"); + auto dynamicSection = _hexagonTargetLayout.findOutputSection(".dynamic"); + if (dynamicSection) + (*dynamicAtomIter)->_virtualAddr = dynamicSection->virtualAddr(); + else + (*dynamicAtomIter)->_virtualAddr = 0; + } + } + +private: + HexagonLinkingContext &_hexagonLinkingContext; + HexagonTargetLayout<ELFT> &_hexagonTargetLayout; +}; + +} // elf +} // lld +#endif // HEXAGON_ELF_WRITERS_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonEncodings.h b/lib/ReaderWriter/ELF/Hexagon/HexagonEncodings.h new file mode 100644 index 0000000000000..3e12786704a25 --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonEncodings.h @@ -0,0 +1,601 @@ +//===- lib/ReaderWriter/ELF/Hexagon/HexagonEncodings.h -------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +Instruction insn_encodings[] = { + { 0xffe00004, 0x40000000, 0x20f8, 0x0 }, + { 0xffe03080, 0x9ca03080, 0xf60, 0x0 }, + { 0xf9e00000, 0x48c00000, 0x61f20ff, 0x0 }, + { 0xf7c02300, 0x13802100, 0x3000fe, 0x0 }, + { 0xffe00000, 0x60c00000, 0x1f18, 0x0 }, + { 0xffe00000, 0x69c00000, 0x1f18, 0x0 }, + { 0xffe02000, 0x43000000, 0x7e0, 0x0 }, + { 0xff602060, 0x3e000060, 0x1f80, 0x0 }, + { 0xffe03000, 0x9ae01000, 0xf60, 0x0 }, + { 0xf9e00000, 0x91600000, 0x6003fe0, 0x0 }, + { 0xffe02084, 0xaf000084, 0x30078, 0x0 }, + { 0xff602060, 0x3e000020, 0x1f80, 0x0 }, + { 0xff602060, 0x3e200040, 0x1f80, 0x0 }, + { 0xf7c02000, 0x10c02000, 0x3000fe, 0x0 }, + { 0xffe00000, 0x60200000, 0x1f18, 0x0 }, + { 0xffe00000, 0x69200000, 0x1f18, 0x0 }, + { 0xffe038c0, 0xada00880, 0x3f, 0x0 }, + { 0xff602000, 0x73002000, 0x1fe0, 0x0 }, + { 0xf7c02000, 0x26c02000, 0x3000fe, 0x0 }, + { 0xffe03880, 0x9f403880, 0x1f0100, 0x0 }, + { 0xf9e00000, 0x48400000, 0x61f20ff, 0x0 }, + { 0xffe02000, 0x41600000, 0x7e0, 0x0 }, + { 0xffe02084, 0xaf000080, 0x30078, 0x0 }, + { 0xf7c02300, 0x13800100, 0x3000fe, 0x0 }, + { 0xffe01804, 0x46a00000, 0x20f8, 0x0 }, + { 0xffe00004, 0x42400000, 0x20f8, 0x0 }, + { 0xf7c02000, 0x22400000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x12402000, 0x3000fe, 0x0 }, + { 0xfc003d18, 0x28003c18, 0x3f00000, 0x1 }, + { 0xffe00000, 0x39000000, 0x201f, 0x0 }, + { 0xff601018, 0xdd400008, 0xfe0, 0x0 }, + { 0xffc0001c, 0x75400000, 0x203fe0, 0x0 }, + { 0xfc003fc7, 0x48003f47, 0x3f00000, 0x1 }, + { 0xffe03080, 0x9ca03000, 0xf60, 0x0 }, + { 0xf9e00000, 0x90800000, 0x6003fe0, 0x0 }, + { 0xf8003fc7, 0x40003fc4, 0x7f00000, 0x1 }, + { 0xfc003e00, 0x68003c00, 0x3f00000, 0x1 }, + { 0xf8003fc7, 0x40003fc5, 0x7f00000, 0x1 }, + { 0xf9e00000, 0x91800000, 0x6003fe0, 0x0 }, + { 0xff602060, 0x3e400060, 0x1f80, 0x0 }, + { 0xff602060, 0x3e000000, 0x1f80, 0x0 }, + { 0xf8003d18, 0x20003c18, 0x7f00000, 0x1 }, + { 0xf8003f00, 0x20003800, 0x7f00000, 0x1 }, + { 0xf8003d18, 0x20003c10, 0x7f00000, 0x1 }, + { 0xff602000, 0x73602000, 0x1fe0, 0x0 }, + { 0xffe03880, 0x9f002080, 0x1f0100, 0x0 }, + { 0xffe02000, 0x47000000, 0x7e0, 0x0 }, + { 0xf9e00000, 0x91400000, 0x6003fe0, 0x0 }, + { 0xffe02080, 0xabc00080, 0x3f, 0x0 }, + { 0xf7c02000, 0x20802000, 0x3000fe, 0x0 }, + { 0xf8003fc7, 0x40003f44, 0x7f00000, 0x1 }, + { 0xffe03884, 0xafa03084, 0x30078, 0x0 }, + { 0xffe03000, 0x9b001000, 0xf60, 0x0 }, + { 0xffe01804, 0x42a00800, 0x20f8, 0x0 }, + { 0xfc003f00, 0x28003100, 0x3f00000, 0x1 }, + { 0xffe02080, 0xab800080, 0x3f, 0x0 }, + { 0xf7c02000, 0x24c00000, 0x3000fe, 0x0 }, + { 0xffe00000, 0x39a00000, 0x201f, 0x0 }, + { 0xf7c02300, 0x13802300, 0x3000fe, 0x0 }, + { 0xffe01804, 0x46a00800, 0x20f8, 0x0 }, + { 0xffe020c0, 0xad602080, 0x3f, 0x0 }, + { 0xfc003f00, 0x28003500, 0x3f00000, 0x1 }, + { 0xfc003f00, 0x28003400, 0x3f00000, 0x1 }, + { 0xffe020c0, 0xad6000c0, 0x3f, 0x0 }, + { 0xffe00000, 0x60000000, 0x1f18, 0x0 }, + { 0xf8003000, 0x40000000, 0x7f00000, 0x1 }, + { 0xffe00000, 0x69000000, 0x1f18, 0x0 }, + { 0xffe03080, 0x9c601080, 0xf60, 0x0 }, + { 0xffe03080, 0x9ce01000, 0xf60, 0x0 }, + { 0xffe03080, 0x9c601000, 0xf60, 0x0 }, + { 0xf7c02000, 0x13402000, 0x3000fe, 0x0 }, + { 0xffe03080, 0x9c603000, 0xf60, 0x0 }, + { 0xf7c02000, 0x21c00000, 0x3000fe, 0x0 }, + { 0xfc003000, 0x68000000, 0x3f00000, 0x1 }, + { 0xf8003800, 0x60002000, 0x7f00000, 0x1 }, + { 0xffe02084, 0xaf802084, 0x30078, 0x0 }, + { 0xfc003000, 0x48000000, 0x3f00000, 0x1 }, + { 0xf7c02300, 0x11c02100, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x12800000, 0x3000fe, 0x0 }, + { 0xfc003e70, 0x28003a40, 0x3f00000, 0x1 }, + { 0xfc003f00, 0x28003300, 0x3f00000, 0x1 }, + { 0xff800000, 0xe0000000, 0x1fe0, 0x0 }, + { 0xff602060, 0x3f400000, 0x1f80, 0x0 }, + { 0xffe00004, 0x42000000, 0x20f8, 0x0 }, + { 0xf8003f00, 0x60003300, 0x7f00000, 0x1 }, + { 0xffe01804, 0x42a00000, 0x20f8, 0x0 }, + { 0xf7c02000, 0x12c00000, 0x3000fe, 0x0 }, + { 0xf0000000, 0x0, 0xfff3fff, 0x0 }, + { 0xff000016, 0xde000016, 0xe020e8, 0x0 }, + { 0xffe03000, 0x9b201000, 0xf60, 0x0 }, + { 0xffe03880, 0xaba00880, 0x3f, 0x0 }, + { 0xf8003e00, 0x40003c00, 0x7f00000, 0x1 }, + { 0xff602060, 0x3f200040, 0x1f80, 0x0 }, + { 0xffe03880, 0x9f203880, 0x1f0100, 0x0 }, + { 0xf7c02000, 0x20c00000, 0x3000fe, 0x0 }, + { 0xf9e01800, 0x48a00800, 0x61f20ff, 0x0 }, + { 0xf9e00000, 0x90a00000, 0x6003fe0, 0x0 }, + { 0xff802000, 0x74802000, 0x1fe0, 0x0 }, + { 0xffe03000, 0x9a401000, 0xf60, 0x0 }, + { 0xf7c02000, 0x10002000, 0x3000fe, 0x0 }, + { 0xf7c03000, 0x14803000, 0x3000fe, 0x0 }, + { 0xffe020c0, 0xad0020c0, 0x3f, 0x0 }, + { 0xffe0001c, 0x75800000, 0x3fe0, 0x0 }, + { 0xf9e01800, 0x48a01000, 0x61f20ff, 0x0 }, + { 0xffe03080, 0x9dc03000, 0xf60, 0x0 }, + { 0xffe03080, 0x9dc03080, 0xf60, 0x0 }, + { 0xffe03080, 0x9dc01000, 0xf60, 0x0 }, + { 0xffe03080, 0x9dc01080, 0xf60, 0x0 }, + { 0xffe03080, 0x9d601000, 0xf60, 0x0 }, + { 0xffe03080, 0x9d601080, 0xf60, 0x0 }, + { 0xffe03080, 0x9d603000, 0xf60, 0x0 }, + { 0xffe03080, 0x9d603080, 0xf60, 0x0 }, + { 0xfc003e00, 0x48003c00, 0x3f00000, 0x1 }, + { 0xffe02084, 0xaf402084, 0x30078, 0x0 }, + { 0xffe00004, 0x46600000, 0x20f8, 0x0 }, + { 0xffe03880, 0x9f203080, 0x1f0100, 0x0 }, + { 0xf8003f00, 0x20003100, 0x7f00000, 0x1 }, + { 0xf7c02000, 0x11402000, 0x3000fe, 0x0 }, + { 0xf8003d08, 0x20003d00, 0x7f00000, 0x1 }, + { 0xffe03080, 0x9ca01080, 0xf60, 0x0 }, + { 0xffe03080, 0x9ca01000, 0xf60, 0x0 }, + { 0xffe00000, 0x38a00000, 0x201f, 0x0 }, + { 0xf7c02300, 0x11800000, 0x3000fe, 0x0 }, + { 0xf7c02300, 0x13c02300, 0x3000fe, 0x0 }, + { 0xffe03080, 0x9ce03000, 0xf60, 0x0 }, + { 0xf9e00000, 0x90e00000, 0x6003fe0, 0x0 }, + { 0xffe02084, 0xaf400080, 0x30078, 0x0 }, + { 0xffe03080, 0x9ce03080, 0xf60, 0x0 }, + { 0xff000000, 0x78000000, 0xdf3fe0, 0x0 }, + { 0xffe03080, 0x9ce01080, 0xf60, 0x0 }, + { 0xffe03880, 0xaba01080, 0x3f, 0x0 }, + { 0xffe020c0, 0xad002080, 0x3f, 0x0 }, + { 0xffe020c0, 0xad0000c0, 0x3f, 0x0 }, + { 0xffe020c0, 0xad000080, 0x3f, 0x0 }, + { 0xf7c02000, 0x25000000, 0x3000fe, 0x0 }, + { 0xff602060, 0x3f200020, 0x1f80, 0x0 }, + { 0xffe02084, 0xafc00084, 0x30078, 0x0 }, + { 0xf7c02000, 0x24400000, 0x3000fe, 0x0 }, + { 0xfc003000, 0x48001000, 0x3f00000, 0x1 }, + { 0xf9e01800, 0xa1a01000, 0x60020ff, 0x0 }, + { 0xff602060, 0x3f000040, 0x1f80, 0x0 }, + { 0xffe02084, 0xaf602084, 0x30078, 0x0 }, + { 0xf8003f00, 0x20003400, 0x7f00000, 0x1 }, + { 0xffe02084, 0xaf400084, 0x30078, 0x0 }, + { 0xffe01804, 0x44a01000, 0x20f8, 0x0 }, + { 0xff602060, 0x3e200000, 0x1f80, 0x0 }, + { 0xf8003e70, 0x20003a70, 0x7f00000, 0x1 }, + { 0xf8003f00, 0x40003e00, 0x7f00000, 0x1 }, + { 0xf8003f00, 0x20003300, 0x7f00000, 0x1 }, + { 0xf7c02300, 0x13800300, 0x3000fe, 0x0 }, + { 0xffe038c0, 0xada00080, 0x3f, 0x0 }, + { 0xf9e00000, 0x49400000, 0x61f3fe0, 0x0 }, + { 0xf8003800, 0x40002800, 0x7f00000, 0x1 }, + { 0xffe038c0, 0xada020c0, 0x3f, 0x0 }, + { 0xffe03884, 0xafa00880, 0x30078, 0x0 }, + { 0xf9e00000, 0x49000000, 0x61f3fe0, 0x0 }, + { 0xff800000, 0xd7000000, 0x6020e0, 0x0 }, + { 0xffc00000, 0xda000000, 0x203fe0, 0x0 }, + { 0xf7c02000, 0x12802000, 0x3000fe, 0x0 }, + { 0xf9e00000, 0x49600000, 0x61f3fe0, 0x0 }, + { 0xffe02000, 0x47400000, 0x7e0, 0x0 }, + { 0xf9e00000, 0x49c00000, 0x61f3fe0, 0x0 }, + { 0xffe03000, 0x9bc01000, 0xf60, 0x0 }, + { 0xf7c02300, 0x13c00100, 0x3000fe, 0x0 }, + { 0xffe03880, 0x9f002880, 0x1f0100, 0x0 }, + { 0xffe03000, 0x9b601000, 0xf60, 0x0 }, + { 0xffe01804, 0x40a00800, 0x20f8, 0x0 }, + { 0xffe00004, 0x42800000, 0x20f8, 0x0 }, + { 0xf7c03000, 0x14800000, 0x3000fe, 0x0 }, + { 0xfc003000, 0x68001000, 0x3f00000, 0x1 }, + { 0xfc003fc7, 0x48003f44, 0x3f00000, 0x1 }, + { 0xfc003fc7, 0x48003f45, 0x3f00000, 0x1 }, + { 0xf7c02000, 0x10800000, 0x3000fe, 0x0 }, + { 0xf8003e70, 0x20003a50, 0x7f00000, 0x1 }, + { 0xf7c02000, 0x21002000, 0x3000fe, 0x0 }, + { 0xf8003fc4, 0x40003fc0, 0x7f00000, 0x1 }, + { 0xf9e00000, 0x48000000, 0x61f20ff, 0x0 }, + { 0xffc0001c, 0x75000010, 0x203fe0, 0x0 }, + { 0xf8003f00, 0x20003800, 0x7f00000, 0x1 }, + { 0xf9e00000, 0xa1800000, 0x60020ff, 0x0 }, + { 0xffc01000, 0x61c00000, 0x202ffe, 0x0 }, + { 0xffe02084, 0xaf402080, 0x30078, 0x0 }, + { 0xffe03880, 0x9f602880, 0x1f0100, 0x0 }, + { 0xfc003f00, 0x68003000, 0x3f00000, 0x1 }, + { 0xfc003f00, 0x68003100, 0x3f00000, 0x1 }, + { 0xff602060, 0x3f200000, 0x1f80, 0x0 }, + { 0xffe03000, 0x9a801000, 0xf60, 0x0 }, + { 0xf7c02000, 0x24802000, 0x3000fe, 0x0 }, + { 0xffe00004, 0x42c00000, 0x20f8, 0x0 }, + { 0xf7c02300, 0x11802000, 0x3000fe, 0x0 }, + { 0xffc01000, 0x61401000, 0x202ffe, 0x0 }, + { 0xffe02000, 0x43c00000, 0x7e0, 0x0 }, + { 0xf7c02000, 0x11400000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x21800000, 0x3000fe, 0x0 }, + { 0xfc003c00, 0x28002c00, 0x3f00000, 0x1 }, + { 0xfc003f00, 0x28003200, 0x3f00000, 0x1 }, + { 0xffe03080, 0x9c803080, 0xf60, 0x0 }, + { 0xf7c03000, 0x14c03000, 0x3000fe, 0x0 }, + { 0xff800000, 0xdb800000, 0x6020e0, 0x0 }, + { 0xf7c02000, 0x22402000, 0x3000fe, 0x0 }, + { 0xffe00004, 0x46800000, 0x20f8, 0x0 }, + { 0xffe00000, 0x69a00000, 0x1f18, 0x0 }, + { 0xfc003e00, 0x68002a00, 0x3f00000, 0x1 }, + { 0xffe00000, 0x60a00000, 0x1f18, 0x0 }, + { 0xf7c02000, 0x25400000, 0x3000fe, 0x0 }, + { 0xfc003e70, 0x28003a70, 0x3f00000, 0x1 }, + { 0xffe03080, 0x9c803000, 0xf60, 0x0 }, + { 0xffc01000, 0x61400000, 0x202ffe, 0x0 }, + { 0xffe01804, 0x42a01000, 0x20f8, 0x0 }, + { 0xffc0001c, 0x75000000, 0x203fe0, 0x0 }, + { 0xffe02084, 0xafc02080, 0x30078, 0x0 }, + { 0xffe03884, 0xafa00884, 0x30078, 0x0 }, + { 0xffe03884, 0xafa02080, 0x30078, 0x0 }, + { 0xffe00000, 0x38c00000, 0x201f, 0x0 }, + { 0xffc01000, 0x61001000, 0x202ffe, 0x0 }, + { 0xf9e00000, 0x48800000, 0x61f20ff, 0x0 }, + { 0xf8003800, 0x40003000, 0x7f00000, 0x1 }, + { 0xf7c03000, 0x15403000, 0x3000fe, 0x0 }, + { 0xf7c03000, 0x15400000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x21000000, 0x3000fe, 0x0 }, + { 0xffe00004, 0x40c00000, 0x20f8, 0x0 }, + { 0xffe01804, 0x46a01000, 0x20f8, 0x0 }, + { 0xf8003d08, 0x20003d08, 0x7f00000, 0x1 }, + { 0xffe038c0, 0xada02080, 0x3f, 0x0 }, + { 0xffe03080, 0x9c203000, 0xf60, 0x0 }, + { 0xfc003800, 0x68002000, 0x3f00000, 0x1 }, + { 0xf9e00000, 0x90600000, 0x6003fe0, 0x0 }, + { 0xf7c03000, 0x14000000, 0x3000fe, 0x0 }, + { 0xf8003e70, 0x20003a40, 0x7f00000, 0x1 }, + { 0xff201800, 0x5c000800, 0xdf20fe, 0x0 }, + { 0xffe02000, 0x41800000, 0x7e0, 0x0 }, + { 0xff800000, 0xdb000000, 0x6020e0, 0x0 }, + { 0xfc003f00, 0x48003e00, 0x3f00000, 0x1 }, + { 0xf7c03000, 0x14002000, 0x3000fe, 0x0 }, + { 0xf7c02300, 0x11800100, 0x3000fe, 0x0 }, + { 0xfc003e00, 0x68002800, 0x3f00000, 0x1 }, + { 0xffe00004, 0x44c00000, 0x20f8, 0x0 }, + { 0xffe03880, 0x9f003880, 0x1f0100, 0x0 }, + { 0xff602000, 0x73402000, 0x1fe0, 0x0 }, + { 0xffe00000, 0x38200000, 0x201f, 0x0 }, + { 0xf7c02000, 0x24800000, 0x3000fe, 0x0 }, + { 0xf7c03000, 0x15001000, 0x3000fe, 0x0 }, + { 0xff800000, 0x7c800000, 0x1f2000, 0x0 }, + { 0xf8003fc7, 0x40003fc6, 0x7f00000, 0x1 }, + { 0xf7c02000, 0x12000000, 0x3000fe, 0x0 }, + { 0xff602000, 0x73202000, 0x1fe0, 0x0 }, + { 0xf7c02300, 0x13c00000, 0x3000fe, 0x0 }, + { 0xff602060, 0x3f400040, 0x1f80, 0x0 }, + { 0xf7c02000, 0x24002000, 0x3000fe, 0x0 }, + { 0xffe02084, 0xaf800080, 0x30078, 0x0 }, + { 0xffe00000, 0x38800000, 0x201f, 0x0 }, + { 0xfc003f00, 0x28003800, 0x3f00000, 0x1 }, + { 0xffe03080, 0x9c801080, 0xf60, 0x0 }, + { 0xffe020c0, 0xad4000c0, 0x3f, 0x0 }, + { 0xffe00000, 0x39400000, 0x201f, 0x0 }, + { 0xf7c02300, 0x13c02100, 0x3000fe, 0x0 }, + { 0xffe020c0, 0xad400080, 0x3f, 0x0 }, + { 0xffe03880, 0x9f603880, 0x1f0100, 0x0 }, + { 0xff000016, 0xde000002, 0xe020e8, 0x0 }, + { 0xfc003d08, 0x28003d00, 0x3f00000, 0x1 }, + { 0xfc003f00, 0x28003000, 0x3f00000, 0x1 }, + { 0xffe03080, 0x9c401000, 0xf60, 0x0 }, + { 0xf7c02000, 0x21402000, 0x3000fe, 0x0 }, + { 0xff201800, 0x5c200800, 0xdf20fe, 0x0 }, + { 0xffe01804, 0x40a01000, 0x20f8, 0x0 }, + { 0xfc003f00, 0x68003300, 0x3f00000, 0x1 }, + { 0xfc003f00, 0x68003200, 0x3f00000, 0x1 }, + { 0xf7c03000, 0x15401000, 0x3000fe, 0x0 }, + { 0xffe01804, 0x44a00800, 0x20f8, 0x0 }, + { 0xf7c02000, 0x26000000, 0x3000fe, 0x0 }, + { 0xffc00000, 0xda400000, 0x203fe0, 0x0 }, + { 0xffe00004, 0x40600000, 0x20f8, 0x0 }, + { 0xffe02080, 0xab600080, 0x3f, 0x0 }, + { 0xf8003f00, 0x20003600, 0x7f00000, 0x1 }, + { 0xf7c02300, 0x11c00300, 0x3000fe, 0x0 }, + { 0xf8003f00, 0x20003700, 0x7f00000, 0x1 }, + { 0xf7c02000, 0x25c00000, 0x3000fe, 0x0 }, + { 0xf7c02300, 0x11800300, 0x3000fe, 0x0 }, + { 0xffe03880, 0x9f802880, 0x1f0100, 0x0 }, + { 0xfc003800, 0x48003000, 0x3f00000, 0x1 }, + { 0xf8003c00, 0x20002c00, 0x7f00000, 0x1 }, + { 0xf7c02000, 0x10400000, 0x3000fe, 0x0 }, + { 0xff602060, 0x3f400060, 0x1f80, 0x0 }, + { 0xffe03080, 0x9c801000, 0xf60, 0x0 }, + { 0xff602060, 0x3e400040, 0x1f80, 0x0 }, + { 0xf7c03000, 0x14402000, 0x3000fe, 0x0 }, + { 0xffe0001c, 0x75800010, 0x3fe0, 0x0 }, + { 0xff000016, 0xde000014, 0xe020e8, 0x0 }, + { 0xf7c02300, 0x11c02000, 0x3000fe, 0x0 }, + { 0xff600018, 0xdd200008, 0x1fe0, 0x0 }, + { 0xff602060, 0x3e200060, 0x1f80, 0x0 }, + { 0xff000016, 0xde000006, 0xe020e8, 0x0 }, + { 0xffe00004, 0x44600000, 0x20f8, 0x0 }, + { 0xf8003e00, 0x60002800, 0x7f00000, 0x1 }, + { 0xfe600000, 0x3c000000, 0x207f, 0x0 }, + { 0xffe03884, 0xafa02884, 0x30078, 0x0 }, + { 0xf7c02300, 0x11802300, 0x3000fe, 0x0 }, + { 0xffe00000, 0x38000000, 0x201f, 0x0 }, + { 0xff200800, 0x5c000000, 0xdf20fe, 0x0 }, + { 0xf7c02000, 0x13400000, 0x3000fe, 0x0 }, + { 0xff200800, 0x5c200000, 0xdf20fe, 0x0 }, + { 0xffe02000, 0x41000000, 0x7e0, 0x0 }, + { 0xffe03880, 0x9fc02880, 0x1f0100, 0x0 }, + { 0xffe00004, 0x46000000, 0x20f8, 0x0 }, + { 0xff602060, 0x3f000020, 0x1f80, 0x0 }, + { 0xfc003d08, 0x28003d08, 0x3f00000, 0x1 }, + { 0xff602060, 0x3f200060, 0x1f80, 0x0 }, + { 0xffe038c0, 0xada028c0, 0x3f, 0x0 }, + { 0xffe038c0, 0xada008c0, 0x3f, 0x0 }, + { 0xf8003f00, 0x20003500, 0x7f00000, 0x1 }, + { 0xfc003fc4, 0x48003f40, 0x3f00000, 0x1 }, + { 0xf9e01800, 0x48a00000, 0x61f20ff, 0x0 }, + { 0xf7c03000, 0x14802000, 0x3000fe, 0x0 }, + { 0xfc003f00, 0x28003900, 0x3f00000, 0x1 }, + { 0xf8003fc7, 0x40003fc7, 0x7f00000, 0x1 }, + { 0xffe02000, 0x45400000, 0x7e0, 0x0 }, + { 0xffe038c0, 0xada02880, 0x3f, 0x0 }, + { 0xffe02084, 0xaf002080, 0x30078, 0x0 }, + { 0xffe03880, 0x9f803880, 0x1f0100, 0x0 }, + { 0xf7c03000, 0x15000000, 0x3000fe, 0x0 }, + { 0xfc003f00, 0x28003700, 0x3f00000, 0x1 }, + { 0xfc003f00, 0x28003600, 0x3f00000, 0x1 }, + { 0xffe02000, 0x47200000, 0x7e0, 0x0 }, + { 0xffe03880, 0xaba00080, 0x3f, 0x0 }, + { 0xffe02084, 0xafc00080, 0x30078, 0x0 }, + { 0xff802000, 0x73800000, 0x1fe0, 0x0 }, + { 0xffe03880, 0x9f202880, 0x1f0100, 0x0 }, + { 0xf8003d18, 0x20003c00, 0x7f00000, 0x1 }, + { 0xf9e00000, 0xa1600000, 0x60020ff, 0x0 }, + { 0xffe00004, 0x44800000, 0x20f8, 0x0 }, + { 0xf7c02000, 0x21802000, 0x3000fe, 0x0 }, + { 0xff000000, 0xd8000000, 0x6020e0, 0x0 }, + { 0xf9e00000, 0xa1000000, 0x60020ff, 0x0 }, + { 0xffe03884, 0xafa00084, 0x30078, 0x0 }, + { 0xff201800, 0x5c201800, 0xdf20fe, 0x0 }, + { 0xff000016, 0xde000010, 0xe020e8, 0x0 }, + { 0xffe03880, 0x9f603080, 0x1f0100, 0x0 }, + { 0xffe02000, 0x41c00000, 0x7e0, 0x0 }, + { 0xf7c02000, 0x20402000, 0x3000fe, 0x0 }, + { 0xff800000, 0xe1000000, 0x1fe0, 0x0 }, + { 0xf9e00000, 0xa1400000, 0x60020ff, 0x0 }, + { 0xf7c03000, 0x14c00000, 0x3000fe, 0x0 }, + { 0xf8003fc7, 0x40003f47, 0x7f00000, 0x1 }, + { 0xffe00004, 0x40800000, 0x20f8, 0x0 }, + { 0xff800000, 0xe1800000, 0x1fe0, 0x0 }, + { 0xf7c02300, 0x11802100, 0x3000fe, 0x0 }, + { 0xf9e00000, 0x49800000, 0x61f3fe0, 0x0 }, + { 0xf7c02000, 0x26400000, 0x3000fe, 0x0 }, + { 0xf8003c00, 0x20002800, 0x7f00000, 0x1 }, + { 0xff902000, 0x7e002000, 0xf1fe0, 0x0 }, + { 0xff902000, 0x7e802000, 0xf1fe0, 0x0 }, + { 0xf9e00000, 0x91c00000, 0x6003fe0, 0x0 }, + { 0xffe03884, 0xafa02880, 0x30078, 0x0 }, + { 0xf7c02000, 0x22000000, 0x3000fe, 0x0 }, + { 0xffe03080, 0x9d203000, 0xf60, 0x0 }, + { 0xf7c02000, 0x26002000, 0x3000fe, 0x0 }, + { 0xff800000, 0xe2000000, 0x1fe0, 0x0 }, + { 0xf7c02000, 0x26c00000, 0x3000fe, 0x0 }, + { 0xff602060, 0x3e400000, 0x1f80, 0x0 }, + { 0xffe00000, 0x38400000, 0x201f, 0x0 }, + { 0xfc003800, 0x48002000, 0x3f00000, 0x1 }, + { 0xff000016, 0xde000000, 0xe020e8, 0x0 }, + { 0xf8003f00, 0x20003000, 0x7f00000, 0x1 }, + { 0xf8003e70, 0x20003a60, 0x7f00000, 0x1 }, + { 0xff902000, 0x7e800000, 0xf1fe0, 0x0 }, + { 0xffe020c0, 0xad6020c0, 0x3f, 0x0 }, + { 0xf7c02300, 0x13802000, 0x3000fe, 0x0 }, + { 0xffe020c0, 0xad600080, 0x3f, 0x0 }, + { 0xff902000, 0x7e000000, 0xf1fe0, 0x0 }, + { 0xf7000000, 0x17000000, 0x3000fe, 0x0 }, + { 0xf7000000, 0x16000000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x25002000, 0x3000fe, 0x0 }, + { 0xfc003fc7, 0x48003fc7, 0x3f00000, 0x1 }, + { 0xffc01000, 0x61801000, 0x202ffe, 0x0 }, + { 0xffe03884, 0xafa03080, 0x30078, 0x0 }, + { 0xf8003fc4, 0x40003f40, 0x7f00000, 0x1 }, + { 0xfc003e70, 0x28003a60, 0x3f00000, 0x1 }, + { 0xf7c02300, 0x13800000, 0x3000fe, 0x0 }, + { 0xffe03880, 0x9f802080, 0x1f0100, 0x0 }, + { 0xf0000000, 0xb0000000, 0xfe03fe0, 0x0 }, + { 0xffe03880, 0x9f402080, 0x1f0100, 0x0 }, + { 0xffe02000, 0x43200000, 0x7e0, 0x0 }, + { 0xffe00000, 0x39800000, 0x201f, 0x0 }, + { 0xffe03880, 0x9fc03880, 0x1f0100, 0x0 }, + { 0xffe02000, 0x45600000, 0x7e0, 0x0 }, + { 0xf9e00000, 0x91200000, 0x6003fe0, 0x0 }, + { 0xffe02000, 0x43600000, 0x7e0, 0x0 }, + { 0xfc003f00, 0x28003800, 0x3f00000, 0x1 }, + { 0xff802000, 0x74000000, 0x1fe0, 0x0 }, + { 0xffe02084, 0xaf002084, 0x30078, 0x0 }, + { 0xff802000, 0x74800000, 0x1fe0, 0x0 }, + { 0xf7c03000, 0x14c02000, 0x3000fe, 0x0 }, + { 0xfe000001, 0x5a000000, 0x1ff3ffe, 0x0 }, + { 0xff602060, 0x3f400020, 0x1f80, 0x0 }, + { 0xf7c02000, 0x10802000, 0x3000fe, 0x0 }, + { 0xffe02084, 0xaf802080, 0x30078, 0x0 }, + { 0xffe00004, 0x46400000, 0x20f8, 0x0 }, + { 0xffe020c0, 0xad800080, 0x3f, 0x0 }, + { 0xffe020c0, 0xad8000c0, 0x3f, 0x0 }, + { 0xf8003fc7, 0x40003f45, 0x7f00000, 0x1 }, + { 0xf8003e00, 0x60002a00, 0x7f00000, 0x1 }, + { 0xffe02084, 0xaf600084, 0x30078, 0x0 }, + { 0xffe03080, 0x9c201000, 0xf60, 0x0 }, + { 0xffe02000, 0x43400000, 0x7e0, 0x0 }, + { 0xffe03080, 0x9c203080, 0xf60, 0x0 }, + { 0xffe02000, 0x41200000, 0x7e0, 0x0 }, + { 0xffe03080, 0x9c201080, 0xf60, 0x0 }, + { 0xf7c02300, 0x11c02300, 0x3000fe, 0x0 }, + { 0xffe03880, 0x9fc03080, 0x1f0100, 0x0 }, + { 0xffe03880, 0x9f402880, 0x1f0100, 0x0 }, + { 0xf8003800, 0x40002000, 0x7f00000, 0x1 }, + { 0xf7c02000, 0x24402000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x20c02000, 0x3000fe, 0x0 }, + { 0xf7c02300, 0x11c00000, 0x3000fe, 0x0 }, + { 0xffe02000, 0x45200000, 0x7e0, 0x0 }, + { 0xf8003f00, 0x20003900, 0x7f00000, 0x1 }, + { 0xf7c02300, 0x11c00100, 0x3000fe, 0x0 }, + { 0xffe02084, 0xaf800084, 0x30078, 0x0 }, + { 0xfe600000, 0x3c200000, 0x207f, 0x0 }, + { 0xf7c02000, 0x26800000, 0x3000fe, 0x0 }, + { 0xffe03880, 0x9f003080, 0x1f0100, 0x0 }, + { 0xffe03884, 0xafa01084, 0x30078, 0x0 }, + { 0xffc00000, 0x76000000, 0x203fe0, 0x0 }, + { 0xff602060, 0x3e000040, 0x1f80, 0x0 }, + { 0xffe020c0, 0xadc020c0, 0x3f, 0x0 }, + { 0xffe00004, 0x44400000, 0x20f8, 0x0 }, + { 0xffe020c0, 0xadc02080, 0x3f, 0x0 }, + { 0xfe600000, 0x3c400000, 0x207f, 0x0 }, + { 0xf7c02000, 0x20400000, 0x3000fe, 0x0 }, + { 0xff800000, 0x7c000000, 0x1fe0, 0x0 }, + { 0xffe03884, 0xafa00080, 0x30078, 0x0 }, + { 0xff201800, 0x5c001800, 0xdf20fe, 0x0 }, + { 0xffe02000, 0x47800000, 0x7e0, 0x0 }, + { 0xff601018, 0xdd400000, 0xfe0, 0x0 }, + { 0xffe020c0, 0xad4020c0, 0x3f, 0x0 }, + { 0xffe020c0, 0xad402080, 0x3f, 0x0 }, + { 0xf8003000, 0x40001000, 0x7f00000, 0x1 }, + { 0xffe02084, 0xafc02084, 0x30078, 0x0 }, + { 0xffe03080, 0x9c403080, 0xf60, 0x0 }, + { 0xfc003e40, 0x28003a00, 0x3f00000, 0x1 }, + { 0xffe038c0, 0xada010c0, 0x3f, 0x0 }, + { 0xffe038c0, 0xada01080, 0x3f, 0x0 }, + { 0xffe038c0, 0xada030c0, 0x3f, 0x0 }, + { 0xffe038c0, 0xada03080, 0x3f, 0x0 }, + { 0xf7c02000, 0x20800000, 0x3000fe, 0x0 }, + { 0xfc003fc7, 0x48003f46, 0x3f00000, 0x1 }, + { 0xffe01804, 0x44a00000, 0x20f8, 0x0 }, + { 0xf7c02000, 0x20002000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x12c02000, 0x3000fe, 0x0 }, + { 0xffe03000, 0x9a601000, 0xf60, 0x0 }, + { 0xffc00000, 0xda800000, 0x203fe0, 0x0 }, + { 0xf9e00000, 0x90400000, 0x6003fe0, 0x0 }, + { 0xffe02000, 0x47600000, 0x7e0, 0x0 }, + { 0xffe03080, 0x9d403000, 0xf60, 0x0 }, + { 0xffe03080, 0x9d403080, 0xf60, 0x0 }, + { 0xffe03080, 0x9d401000, 0xf60, 0x0 }, + { 0xffe03080, 0x9d401080, 0xf60, 0x0 }, + { 0xffe02000, 0x41400000, 0x7e0, 0x0 }, + { 0xff800000, 0xdf800000, 0x6020e0, 0x0 }, + { 0xffc01000, 0x61000000, 0x202ffe, 0x0 }, + { 0xffe03880, 0x9f202080, 0x1f0100, 0x0 }, + { 0xfc003fc7, 0x48003fc6, 0x3f00000, 0x1 }, + { 0xfe000000, 0x7a000000, 0x1fe0, 0x0 }, + { 0xffff0000, 0x6a490000, 0x1f80, 0x0 }, + { 0xff802000, 0x73000000, 0x1fe0, 0x0 }, + { 0xff602060, 0x3e200020, 0x1f80, 0x0 }, + { 0xf7c02000, 0x24000000, 0x3000fe, 0x0 }, + { 0xf8003e40, 0x20003a00, 0x7f00000, 0x1 }, + { 0xf7c03000, 0x14401000, 0x3000fe, 0x0 }, + { 0xf8003f00, 0x20003200, 0x7f00000, 0x1 }, + { 0xffc00000, 0x76400000, 0x203fe0, 0x0 }, + { 0xf7c02000, 0x22002000, 0x3000fe, 0x0 }, + { 0xffc01000, 0x61c01000, 0x202ffe, 0x0 }, + { 0xf7c03000, 0x14801000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x12002000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x10402000, 0x3000fe, 0x0 }, + { 0xff201800, 0x5d200000, 0xdf20fe, 0x0 }, + { 0xf7c02000, 0x21400000, 0x3000fe, 0x0 }, + { 0xff201800, 0x5d000000, 0xdf20fe, 0x0 }, + { 0xffe02000, 0x45c00000, 0x7e0, 0x0 }, + { 0xf7c02000, 0x25802000, 0x3000fe, 0x0 }, + { 0xfc003e70, 0x28003a50, 0x3f00000, 0x1 }, + { 0xf7c02300, 0x13c00300, 0x3000fe, 0x0 }, + { 0xf9e01800, 0xa1a00800, 0x60020ff, 0x0 }, + { 0xffe02000, 0x43800000, 0x7e0, 0x0 }, + { 0xfc003fc4, 0x48003fc0, 0x3f00000, 0x1 }, + { 0xff800000, 0xe2800000, 0x1fe0, 0x0 }, + { 0xf7c02300, 0x13c02000, 0x3000fe, 0x0 }, + { 0xffe03080, 0x9d803080, 0xf60, 0x0 }, + { 0xffe03080, 0x9d803000, 0xf60, 0x0 }, + { 0xffe03080, 0x9d801080, 0xf60, 0x0 }, + { 0xf8003fc4, 0x40003f00, 0x7f00000, 0x1 }, + { 0xffe00000, 0x39c00000, 0x201f, 0x0 }, + { 0xffe03080, 0x9d203080, 0xf60, 0x0 }, + { 0xffe02080, 0xab000080, 0x3f, 0x0 }, + { 0xf8003e00, 0x60003c00, 0x7f00000, 0x1 }, + { 0xffe03880, 0x9f602080, 0x1f0100, 0x0 }, + { 0xffc00000, 0x76800000, 0x203fe0, 0x0 }, + { 0xffe03884, 0xafa02084, 0x30078, 0x0 }, + { 0xf7c02000, 0x13002000, 0x3000fe, 0x0 }, + { 0xf9e00000, 0x91000000, 0x6003fe0, 0x0 }, + { 0xffe03080, 0x9d201080, 0xf60, 0x0 }, + { 0xf7c03000, 0x15002000, 0x3000fe, 0x0 }, + { 0xf8003000, 0x60000000, 0x7f00000, 0x1 }, + { 0xffc01000, 0x61800000, 0x202ffe, 0x0 }, + { 0xf7c03000, 0x14400000, 0x3000fe, 0x0 }, + { 0xffe03000, 0x9b401000, 0xf60, 0x0 }, + { 0xf7c03000, 0x14003000, 0x3000fe, 0x0 }, + { 0xffe03880, 0x9fc02080, 0x1f0100, 0x0 }, + { 0xfc003fc4, 0x48003f00, 0x3f00000, 0x1 }, + { 0xffe02000, 0x45000000, 0x7e0, 0x0 }, + { 0xfc003800, 0x48002800, 0x3f00000, 0x1 }, + { 0xfc003fc7, 0x48003fc5, 0x3f00000, 0x1 }, + { 0xfc003d18, 0x28003c00, 0x3f00000, 0x1 }, + { 0xfc003fc7, 0x48003fc4, 0x3f00000, 0x1 }, + { 0xf8003f00, 0x60003200, 0x7f00000, 0x1 }, + { 0xffe02084, 0xaf600080, 0x30078, 0x0 }, + { 0xf9e01800, 0xa1a00000, 0x60020ff, 0x0 }, + { 0xf7c03000, 0x14001000, 0x3000fe, 0x0 }, + { 0xf7c03000, 0x14c01000, 0x3000fe, 0x0 }, + { 0xffe00004, 0x46c00000, 0x20f8, 0x0 }, + { 0xf7c03000, 0x15003000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x10000000, 0x3000fe, 0x0 }, + { 0xf8003d18, 0x20003c08, 0x7f00000, 0x1 }, + { 0xffc0001c, 0x75400010, 0x203fe0, 0x0 }, + { 0xf9e00000, 0x48600000, 0x61f20ff, 0x0 }, + { 0xffe03080, 0x9c603080, 0xf60, 0x0 }, + { 0xfe000000, 0x58000000, 0x1ff3ffe, 0x0 }, + { 0xffe03000, 0x9a201000, 0xf60, 0x0 }, + { 0xffe00000, 0x69e00000, 0x1f18, 0x0 }, + { 0xffe020c0, 0xad802080, 0x3f, 0x0 }, + { 0xffe02000, 0x47c00000, 0x7e0, 0x0 }, + { 0xffe00000, 0x60e00000, 0x1f18, 0x0 }, + { 0xf7c03000, 0x15402000, 0x3000fe, 0x0 }, + { 0xffe020c0, 0xad8020c0, 0x3f, 0x0 }, + { 0xff000016, 0xde000012, 0xe020e8, 0x0 }, + { 0xf7c02000, 0x25c02000, 0x3000fe, 0x0 }, + { 0xf8003f00, 0x60003100, 0x7f00000, 0x1 }, + { 0xf8003f00, 0x60003000, 0x7f00000, 0x1 }, + { 0xf7c02000, 0x25800000, 0x3000fe, 0x0 }, + { 0xf7c03000, 0x14403000, 0x3000fe, 0x0 }, + { 0xfc003d18, 0x28003c08, 0x3f00000, 0x1 }, + { 0xffe03880, 0x9f403080, 0x1f0100, 0x0 }, + { 0xf7c02000, 0x25402000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x10c00000, 0x3000fe, 0x0 }, + { 0xffe02000, 0x45800000, 0x7e0, 0x0 }, + { 0xffe03880, 0x9f803080, 0x1f0100, 0x0 }, + { 0xffe03080, 0x9d001000, 0xf60, 0x0 }, + { 0xffe03080, 0x9d001080, 0xf60, 0x0 }, + { 0xffe03080, 0x9d003000, 0xf60, 0x0 }, + { 0xffe03080, 0x9d003080, 0xf60, 0x0 }, + { 0xffe03080, 0x9d801000, 0xf60, 0x0 }, + { 0xf9e00000, 0x49200000, 0x61f3fe0, 0x0 }, + { 0xf9e00000, 0xa1c00000, 0x60020ff, 0x0 }, + { 0xf9e00000, 0x90200000, 0x6003fe0, 0x0 }, + { 0xffe03080, 0x9d201000, 0xf60, 0x0 }, + { 0xffe03884, 0xafa01080, 0x30078, 0x0 }, + { 0xffe02084, 0xaf602080, 0x30078, 0x0 }, + { 0xffe038c0, 0xada000c0, 0x3f, 0x0 }, + { 0xffe02080, 0xab400080, 0x3f, 0x0 }, + { 0xff000016, 0xde000004, 0xe020e8, 0x0 }, + { 0xffe00004, 0x44000000, 0x20f8, 0x0 }, + { 0xf7c02000, 0x20000000, 0x3000fe, 0x0 }, + { 0xfc003d18, 0x28003c10, 0x3f00000, 0x1 }, + { 0xff600018, 0xdd000008, 0x1fe0, 0x0 }, + { 0xffe020c0, 0xadc000c0, 0x3f, 0x0 }, + { 0xffe020c0, 0xadc00080, 0x3f, 0x0 }, + { 0xffe03000, 0x9b801000, 0xf60, 0x0 }, + { 0xf8003fc7, 0x40003f46, 0x7f00000, 0x1 }, + { 0xf7c02000, 0x21c02000, 0x3000fe, 0x0 }, + { 0xffe01804, 0x40a00000, 0x20f8, 0x0 }, + { 0xf7c02000, 0x26402000, 0x3000fe, 0x0 }, + { 0xffe03080, 0x9c401080, 0xf60, 0x0 }, + { 0xffe00000, 0x39200000, 0x201f, 0x0 }, + { 0xffe03080, 0x9c403000, 0xf60, 0x0 }, + { 0xf7c02000, 0x11002000, 0x3000fe, 0x0 }, + { 0xfc003c00, 0x28002800, 0x3f00000, 0x1 }, + { 0xffe00004, 0x40400000, 0x20f8, 0x0 }, + { 0xf7c02000, 0x26802000, 0x3000fe, 0x0 }, + { 0xf7c02000, 0x13000000, 0x3000fe, 0x0 }, + { 0xffe00004, 0x42600000, 0x20f8, 0x0 }, + { 0xf8003000, 0x60001000, 0x7f00000, 0x1 }, + { 0xff602060, 0x3e400020, 0x1f80, 0x0 }, + { 0xff602060, 0x3f000000, 0x1f80, 0x0 }, + { 0xf7c02000, 0x24c02000, 0x3000fe, 0x0 }, + { 0xff802000, 0x74002000, 0x1fe0, 0x0 }, + { 0xf8003800, 0x20002000, 0x7f00000, 0x1 }, + { 0xffe03000, 0x9aa01000, 0xf60, 0x0 }, + { 0xf7c02000, 0x12400000, 0x3000fe, 0x0 }, + { 0xff602060, 0x3f000060, 0x1f80, 0x0 }, + { 0xf7c02000, 0x11000000, 0x3000fe, 0x0 }, +}; diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableAtoms.h b/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableAtoms.h new file mode 100644 index 0000000000000..a2505aa460c5b --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableAtoms.h @@ -0,0 +1,29 @@ +//===- lib/ReaderWriter/ELF/Hexagon/HexagonExecutableAtoms.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_HEXAGON_HEXAGON_EXECUTABLE_ATOM_H +#define LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_EXECUTABLE_ATOM_H + +#include "ELFFile.h" + +namespace lld { +namespace elf { +typedef llvm::object::ELFType<llvm::support::little, 2, false> HexagonELFType; +class HexagonLinkingContext; + +template <class HexagonELFType> class HexagonRuntimeFile + : public RuntimeFile<HexagonELFType> { +public: + HexagonRuntimeFile(HexagonLinkingContext &context) + : RuntimeFile<HexagonELFType>(context, "Hexagon runtime file") {} +}; +} // elf +} // lld + +#endif // LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_EXECUTABLE_ATOM_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableWriter.h b/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableWriter.h new file mode 100644 index 0000000000000..0848e64166faa --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonExecutableWriter.h @@ -0,0 +1,86 @@ +//===- lib/ReaderWriter/ELF/Hexagon/HexagonExecutableWriter.h -------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef HEXAGON_EXECUTABLE_WRITER_H +#define HEXAGON_EXECUTABLE_WRITER_H + +#include "ExecutableWriter.h" +#include "HexagonELFWriters.h" +#include "HexagonExecutableAtoms.h" +#include "HexagonLinkingContext.h" + +namespace lld { +namespace elf { + +template <typename ELFT> class HexagonTargetLayout; + +template <class ELFT> +class HexagonExecutableWriter : public ExecutableWriter<ELFT>, + public HexagonELFWriter<ELFT> { +public: + HexagonExecutableWriter(HexagonLinkingContext &context, + HexagonTargetLayout<ELFT> &layout); + +protected: + // Add any runtime files and their atoms to the output + virtual bool createImplicitFiles(std::vector<std::unique_ptr<File>> &); + + virtual void finalizeDefaultAtomValues(); + + virtual std::error_code setELFHeader() { + ExecutableWriter<ELFT>::setELFHeader(); + HexagonELFWriter<ELFT>::setELFHeader(*this->_elfHeader); + return std::error_code(); + } + +private: + void addDefaultAtoms() { + _hexagonRuntimeFile->addAbsoluteAtom("_SDA_BASE_"); + if (this->_context.isDynamic()) { + _hexagonRuntimeFile->addAbsoluteAtom("_GLOBAL_OFFSET_TABLE_"); + _hexagonRuntimeFile->addAbsoluteAtom("_DYNAMIC"); + } + } + + HexagonLinkingContext &_hexagonLinkingContext; + HexagonTargetLayout<ELFT> &_hexagonTargetLayout; + std::unique_ptr<HexagonRuntimeFile<ELFT>> _hexagonRuntimeFile; +}; + +template <class ELFT> +HexagonExecutableWriter<ELFT>::HexagonExecutableWriter( + HexagonLinkingContext &context, HexagonTargetLayout<ELFT> &layout) + : ExecutableWriter<ELFT>(context, layout), + HexagonELFWriter<ELFT>(context, layout), _hexagonLinkingContext(context), + _hexagonTargetLayout(layout), + _hexagonRuntimeFile(new HexagonRuntimeFile<ELFT>(context)) {} + +template <class ELFT> +bool HexagonExecutableWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + ExecutableWriter<ELFT>::createImplicitFiles(result); + // Add the default atoms as defined for hexagon + addDefaultAtoms(); + result.push_back(std::move(_hexagonRuntimeFile)); + return true; +} + +template <class ELFT> +void HexagonExecutableWriter<ELFT>::finalizeDefaultAtomValues() { + // Finalize the atom values that are part of the parent. + ExecutableWriter<ELFT>::finalizeDefaultAtomValues(); + auto sdabaseAtomIter = _hexagonTargetLayout.findAbsoluteAtom("_SDA_BASE_"); + (*sdabaseAtomIter)->_virtualAddr = + _hexagonTargetLayout.getSDataSection()->virtualAddr(); + HexagonELFWriter<ELFT>::finalizeHexagonRuntimeAtomValues(); +} + +} // namespace elf +} // namespace lld + +#endif // HEXAGON_EXECUTABLE_WRITER_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.cpp b/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.cpp new file mode 100644 index 0000000000000..7eacb2b44c3b2 --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.cpp @@ -0,0 +1,25 @@ +//===- lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.cpp -------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "HexagonLinkingContext.h" +#include "HexagonTargetHandler.h" + +using namespace lld::elf; + +std::unique_ptr<lld::ELFLinkingContext> +HexagonLinkingContext::create(llvm::Triple triple) { + if (triple.getArch() == llvm::Triple::hexagon) + return std::unique_ptr<lld::ELFLinkingContext>( + new HexagonLinkingContext(triple)); + return nullptr; +} + +HexagonLinkingContext::HexagonLinkingContext(llvm::Triple triple) + : ELFLinkingContext(triple, std::unique_ptr<TargetHandlerBase>( + new HexagonTargetHandler(*this))) {} diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.h b/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.h new file mode 100644 index 0000000000000..c920cdf153aaf --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.h @@ -0,0 +1,69 @@ +//===- lib/ReaderWriter/ELF/Hexagon/HexagonLinkingContext.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_HEXAGON_HEXAGON_LINKING_CONTEXT_H +#define LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_LINKING_CONTEXT_H + +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/ELF.h" + +namespace lld { +namespace elf { + +typedef llvm::object::ELFType<llvm::support::little, 2, false> HexagonELFType; + +class HexagonLinkingContext final : public ELFLinkingContext { +public: + static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + HexagonLinkingContext(llvm::Triple triple); + + void addPasses(PassManager &) override; + + bool isDynamicRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + switch (r.kindValue()) { + case llvm::ELF::R_HEX_RELATIVE: + case llvm::ELF::R_HEX_GLOB_DAT: + return true; + default: + return false; + } + } + + bool isPLTRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + switch (r.kindValue()) { + case llvm::ELF::R_HEX_JMP_SLOT: + return true; + default: + return false; + } + } + + /// \brief Hexagon has only one relative relocation + /// a) for supporting relative relocs - R_HEX_RELATIVE + bool isRelativeReloc(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + switch (r.kindValue()) { + case llvm::ELF::R_HEX_RELATIVE: + return true; + default: + return false; + } + } +}; + +} // elf +} // lld + +#endif // LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_LINKING_CONTEXT_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationFunctions.h b/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationFunctions.h new file mode 100644 index 0000000000000..2b9e25ce363b5 --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationFunctions.h @@ -0,0 +1,49 @@ +//===- HexagonRelocationFunction.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_HEXAGON_HEXAGON_RELOCATION_FUNCTIONS_H +#define LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_RELOCATION_FUNCTIONS_H + +namespace lld { +namespace elf { + +/// \brief HexagonInstruction which is used to store various values +typedef struct { + uint32_t insnMask; + uint32_t insnCmpMask; + uint32_t insnBitMask; + bool isDuplex; +} Instruction; + +#include "HexagonEncodings.h" + +#define FINDV4BITMASK(INSN) \ + findBitMask((uint32_t) * ((llvm::support::ulittle32_t *) INSN), \ + insn_encodings, \ + sizeof(insn_encodings) / sizeof(Instruction)) + +/// \brief finds the scatter Bits that need to be used to apply relocations +inline uint32_t +findBitMask(uint32_t insn, Instruction *encodings, int32_t numInsns) { + for (int32_t i = 0; i < numInsns; i++) { + if (((insn & 0xc000) == 0) && !(encodings[i].isDuplex)) + continue; + + if (((insn & 0xc000) != 0) && (encodings[i].isDuplex)) + continue; + + if (((encodings[i].insnMask) & insn) == encodings[i].insnCmpMask) + return encodings[i].insnBitMask; + } + llvm_unreachable("found unknown instruction"); +} + +} // elf +} // lld + +#endif // LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_RELOCATION_FUNCTIONS_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.cpp b/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.cpp new file mode 100644 index 0000000000000..21967d356a311 --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.cpp @@ -0,0 +1,350 @@ +//===- lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.cpp ---------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "HexagonLinkingContext.h" +#include "HexagonRelocationFunctions.h" +#include "HexagonTargetHandler.h" +#include "HexagonRelocationHandler.h" +#include "llvm/Support/Endian.h" + +using namespace lld; +using namespace lld::elf; +using namespace llvm::ELF; +using namespace llvm::support::endian; + +#define APPLY_RELOC(result) \ + write32le(location, result | read32le(location)); + +static int relocBNPCREL(uint8_t *location, uint64_t P, uint64_t S, uint64_t A, + int32_t nBits) { + int32_t result = (uint32_t)(((S + A) - P) >> 2); + int32_t range = 1 << nBits; + if (result < range && result > -range) { + result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; + } + return 1; +} + +/// \brief Word32_LO: 0x00c03fff : (S + A) : Truncate +static int relocLO16(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { + uint32_t result = (uint32_t)(S + A); + result = lld::scatterBits<int32_t>(result, 0x00c03fff); + APPLY_RELOC(result); + return 0; +} + +/// \brief Word32_LO: 0x00c03fff : (S + A) >> 16 : Truncate +static int relocHI16(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { + uint32_t result = (uint32_t)((S + A) >> 16); + result = lld::scatterBits<int32_t>(result, 0x00c03fff); + APPLY_RELOC(result); + return 0; +} + +/// \brief Word32: 0xffffffff : (S + A) : Truncate +static int reloc32(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { + uint32_t result = (uint32_t)(S + A); + APPLY_RELOC(result); + return 0; +} + +static int reloc32_6_X(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { + int64_t result = ((S + A) >> 6); + int64_t range = ((int64_t)1) << 32; + if (result > range) + return 1; + result = lld::scatterBits<int32_t>(result, 0xfff3fff); + APPLY_RELOC(result); + return 0; +} + +// R_HEX_B32_PCREL_X +static int relocHexB32PCRELX(uint8_t *location, uint64_t P, uint64_t S, + uint64_t A) { + int64_t result = ((S + A - P) >> 6); + result = lld::scatterBits<int32_t>(result, 0xfff3fff); + APPLY_RELOC(result); + return 0; +} + +// R_HEX_BN_PCREL_X +static int relocHexBNPCRELX(uint8_t *location, uint64_t P, uint64_t S, + uint64_t A, int nbits) { + int32_t result = ((S + A - P) & 0x3f); + int32_t range = 1 << nbits; + if (result < range && result > -range) { + result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; + } + return 1; +} + +// R_HEX_6_PCREL_X +static int relocHex6PCRELX(uint8_t *location, uint64_t P, uint64_t S, + uint64_t A) { + int32_t result = (S + A - P); + result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; +} + +// R_HEX_N_X : Word32_U6 : (S + A) : Unsigned Truncate +static int relocHex_N_X(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { + uint32_t result = (S + A); + result = lld::scatterBits<uint32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; +} + +// GP REL relocations +static int relocHexGPRELN(uint8_t *location, uint64_t P, uint64_t S, uint64_t A, + uint64_t GP, int nShiftBits) { + int32_t result = (int64_t)((S + A - GP) >> nShiftBits); + int32_t range = 1L << 16; + if (result <= range) { + result = lld::scatterBits<uint32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; + } + return 1; +} + +/// \brief Word32_LO: 0x00c03fff : (G) : Truncate +static int relocHexGOTLO16(uint8_t *location, uint64_t A, uint64_t GOT) { + int32_t result = (int32_t)(A-GOT); + result = lld::scatterBits<int32_t>(result, 0x00c03fff); + APPLY_RELOC(result); + return 0; +} + +/// \brief Word32_LO: 0x00c03fff : (G) >> 16 : Truncate +static int relocHexGOTHI16(uint8_t *location, uint64_t A, uint64_t GOT) { + int32_t result = (int32_t)((A-GOT) >> 16); + result = lld::scatterBits<int32_t>(result, 0x00c03fff); + APPLY_RELOC(result); + return 0; +} + +/// \brief Word32: 0xffffffff : (G) : Truncate +static int relocHexGOT32(uint8_t *location, uint64_t A, uint64_t GOT) { + int32_t result = (int32_t)(GOT - A); + APPLY_RELOC(result); + return 0; +} + +/// \brief Word32_U16 : (G) : Truncate +static int relocHexGOT16(uint8_t *location, uint64_t A, uint64_t GOT) { + int32_t result = (int32_t)(GOT-A); + int32_t range = 1L << 16; + if (result <= range) { + result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; + } + return 1; +} + +static int relocHexGOT32_6_X(uint8_t *location, uint64_t A, uint64_t GOT) { + int32_t result = (int32_t)((A-GOT) >> 6); + result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; +} + +static int relocHexGOT16_X(uint8_t *location, uint64_t A, uint64_t GOT) { + int32_t result = (int32_t)(A-GOT); + int32_t range = 1L << 6; + if (result <= range) { + result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; + } + return 1; +} + +static int relocHexGOT11_X(uint8_t *location, uint64_t A, uint64_t GOT) { + uint32_t result = (uint32_t)(A-GOT); + result = lld::scatterBits<uint32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; +} + +static int relocHexGOTRELSigned(uint8_t *location, uint64_t P, uint64_t S, + uint64_t A, uint64_t GOT, int shiftBits = 0) { + int32_t result = (int32_t)((S + A - GOT) >> shiftBits); + result = lld::scatterBits<int32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; +} + +static int relocHexGOTRELUnsigned(uint8_t *location, uint64_t P, uint64_t S, + uint64_t A, uint64_t GOT, int shiftBits = 0) { + uint32_t result = (uint32_t)((S + A - GOT) >> shiftBits); + result = lld::scatterBits<uint32_t>(result, FINDV4BITMASK(location)); + APPLY_RELOC(result); + return 0; +} + +static int relocHexGOTREL_HILO16(uint8_t *location, uint64_t P, uint64_t S, + uint64_t A, uint64_t GOT, int shiftBits = 0) { + int32_t result = (int32_t)((S + A - GOT) >> shiftBits); + result = lld::scatterBits<int32_t>(result, 0x00c03fff); + APPLY_RELOC(result); + return 0; +} + +static int relocHexGOTREL_32(uint8_t *location, uint64_t P, uint64_t S, + uint64_t A, uint64_t GOT) { + int32_t result = (int32_t)(S + A - GOT); + APPLY_RELOC(result); + return 0; +} + +std::error_code HexagonTargetRelocationHandler::applyRelocation( + ELFWriter &writer, llvm::FileOutputBuffer &buf, const lld::AtomLayout &atom, + const Reference &ref) const { + uint8_t *atomContent = buf.getBufferStart() + atom._fileOffset; + uint8_t *location = atomContent + ref.offsetInAtom(); + uint64_t targetVAddress = writer.addressOfAtom(ref.target()); + uint64_t relocVAddress = atom._virtualAddr + ref.offsetInAtom(); + + if (ref.kindNamespace() != Reference::KindNamespace::ELF) + return std::error_code(); + assert(ref.kindArch() == Reference::KindArch::Hexagon); + switch (ref.kindValue()) { + case R_HEX_B22_PCREL: + relocBNPCREL(location, relocVAddress, targetVAddress, ref.addend(), 21); + break; + case R_HEX_B15_PCREL: + relocBNPCREL(location, relocVAddress, targetVAddress, ref.addend(), 14); + break; + case R_HEX_B9_PCREL: + relocBNPCREL(location, relocVAddress, targetVAddress, ref.addend(), 8); + break; + case R_HEX_LO16: + relocLO16(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_HEX_HI16: + relocHI16(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_HEX_32: + reloc32(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_HEX_32_6_X: + reloc32_6_X(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_HEX_B32_PCREL_X: + relocHexB32PCRELX(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_HEX_B22_PCREL_X: + relocHexBNPCRELX(location, relocVAddress, targetVAddress, ref.addend(), 21); + break; + case R_HEX_B15_PCREL_X: + relocHexBNPCRELX(location, relocVAddress, targetVAddress, ref.addend(), 14); + break; + case R_HEX_B13_PCREL_X: + relocHexBNPCRELX(location, relocVAddress, targetVAddress, ref.addend(), 12); + break; + case R_HEX_B9_PCREL_X: + relocHexBNPCRELX(location, relocVAddress, targetVAddress, ref.addend(), 8); + break; + case R_HEX_B7_PCREL_X: + relocHexBNPCRELX(location, relocVAddress, targetVAddress, ref.addend(), 6); + break; + case R_HEX_GPREL16_0: + relocHexGPRELN(location, relocVAddress, targetVAddress, ref.addend(), + _hexagonTargetLayout.getSDataSection()->virtualAddr(), 0); + break; + case R_HEX_GPREL16_1: + relocHexGPRELN(location, relocVAddress, targetVAddress, ref.addend(), + _hexagonTargetLayout.getSDataSection()->virtualAddr(), 1); + break; + case R_HEX_GPREL16_2: + relocHexGPRELN(location, relocVAddress, targetVAddress, ref.addend(), + _hexagonTargetLayout.getSDataSection()->virtualAddr(), 2); + break; + case R_HEX_GPREL16_3: + relocHexGPRELN(location, relocVAddress, targetVAddress, ref.addend(), + _hexagonTargetLayout.getSDataSection()->virtualAddr(), 3); + break; + case R_HEX_16_X: + case R_HEX_12_X: + case R_HEX_11_X: + case R_HEX_10_X: + case R_HEX_9_X: + case R_HEX_8_X: + case R_HEX_7_X: + case R_HEX_6_X: + relocHex_N_X(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_HEX_6_PCREL_X: + relocHex6PCRELX(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_HEX_JMP_SLOT: + case R_HEX_GLOB_DAT: + break; + case R_HEX_GOTREL_32: + relocHexGOTREL_32(location, relocVAddress, targetVAddress, ref.addend(), + _hexagonTargetLayout.getGOTSymAddr()); + break; + case R_HEX_GOTREL_LO16: + relocHexGOTREL_HILO16(location, relocVAddress, targetVAddress, ref.addend(), + _hexagonTargetLayout.getGOTSymAddr()); + break; + case R_HEX_GOTREL_HI16: + relocHexGOTREL_HILO16(location, relocVAddress, targetVAddress, ref.addend(), + _hexagonTargetLayout.getGOTSymAddr(), 16); + break; + case R_HEX_GOT_LO16: + relocHexGOTLO16(location, targetVAddress, + _hexagonTargetLayout.getGOTSymAddr()); + break; + case R_HEX_GOT_HI16: + relocHexGOTHI16(location, targetVAddress, + _hexagonTargetLayout.getGOTSymAddr()); + break; + case R_HEX_GOT_32: + relocHexGOT32(location, targetVAddress, + _hexagonTargetLayout.getGOTSymAddr()); + break; + case R_HEX_GOT_16: + relocHexGOT16(location, targetVAddress, + _hexagonTargetLayout.getGOTSymAddr()); + break; + case R_HEX_GOT_32_6_X: + relocHexGOT32_6_X(location, targetVAddress, + _hexagonTargetLayout.getGOTSymAddr()); + break; + case R_HEX_GOT_16_X: + relocHexGOT16_X(location, targetVAddress, + _hexagonTargetLayout.getGOTSymAddr()); + break; + case R_HEX_GOT_11_X: + relocHexGOT11_X(location, targetVAddress, + _hexagonTargetLayout.getGOTSymAddr()); + break; + case R_HEX_GOTREL_32_6_X: + relocHexGOTRELSigned(location, relocVAddress, targetVAddress, ref.addend(), + _hexagonTargetLayout.getGOTSymAddr(), 6); + break; + case R_HEX_GOTREL_16_X: + case R_HEX_GOTREL_11_X: + relocHexGOTRELUnsigned(location, relocVAddress, targetVAddress, + ref.addend(), _hexagonTargetLayout.getGOTSymAddr()); + break; + + default: + return make_unhandled_reloc_error(); + } + + return std::error_code(); +} diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.h b/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.h new file mode 100644 index 0000000000000..4795d0264b9cd --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.h @@ -0,0 +1,35 @@ +//===- lld/ReaderWriter/ELF/Hexagon/HexagonRelocationHandler.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_HEXAGON_HEXAGON_RELOCATION_HANDLER_H +#define LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_RELOCATION_HANDLER_H + +#include "HexagonSectionChunks.h" +#include "HexagonTargetHandler.h" +#include "lld/ReaderWriter/RelocationHelperFunctions.h" + +namespace lld { +namespace elf { + +class HexagonTargetHandler; + +class HexagonTargetRelocationHandler final : public TargetRelocationHandler { +public: + HexagonTargetRelocationHandler(HexagonTargetLayout<HexagonELFType> &layout) + : _hexagonTargetLayout(layout) {} + + std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, + const lld::AtomLayout &, + const Reference &) const override; + +private: + HexagonTargetLayout<HexagonELFType> &_hexagonTargetLayout; +}; +} // elf +} // lld +#endif diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonSectionChunks.h b/lib/ReaderWriter/ELF/Hexagon/HexagonSectionChunks.h new file mode 100644 index 0000000000000..5b3fbbbd899be --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonSectionChunks.h @@ -0,0 +1,86 @@ +//===- lib/ReaderWriter/ELF/Hexagon/HexagonSectionChunks.h-----------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef HEXAGON_SECTION_CHUNKS_H +#define HEXAGON_SECTION_CHUNKS_H + +#include "HexagonTargetHandler.h" + +namespace lld { +namespace elf { +template <typename ELFT> class HexagonTargetLayout; +class HexagonLinkingContext; + +/// \brief Handle Hexagon SData section +template <class HexagonELFType> +class SDataSection : public AtomSection<HexagonELFType> { +public: + SDataSection(const HexagonLinkingContext &context) + : AtomSection<HexagonELFType>( + context, ".sdata", DefinedAtom::typeDataFast, 0, + HexagonTargetLayout<HexagonELFType>::ORDER_SDATA) { + this->_type = SHT_PROGBITS; + this->_flags = SHF_ALLOC | SHF_WRITE; + this->_alignment = 4096; + } + + /// \brief Finalize the section contents before writing + virtual void doPreFlight(); + + /// \brief Does this section have an output segment. + virtual bool hasOutputSegment() { return true; } + + const lld::AtomLayout *appendAtom(const Atom *atom) { + const DefinedAtom *definedAtom = cast<DefinedAtom>(atom); + DefinedAtom::Alignment atomAlign = definedAtom->alignment(); + uint64_t alignment = 1u << atomAlign.powerOf2; + this->_atoms.push_back(new (this->_alloc) lld::AtomLayout(atom, 0, 0)); + // Set the section alignment to the largest alignment + // std::max doesn't support uint64_t + if (this->_alignment < alignment) + this->_alignment = alignment; + return (this->_atoms.back()); + } + +}; // SDataSection + +template <class HexagonELFType> +void SDataSection<HexagonELFType>::doPreFlight() { + // sort the atoms on the alignments they have been set + std::stable_sort(this->_atoms.begin(), this->_atoms.end(), + [](const lld::AtomLayout * A, + const lld::AtomLayout * B) { + const DefinedAtom *definedAtomA = cast<DefinedAtom>(A->_atom); + const DefinedAtom *definedAtomB = cast<DefinedAtom>(B->_atom); + int64_t alignmentA = 1 << definedAtomA->alignment().powerOf2; + int64_t alignmentB = 1 << definedAtomB->alignment().powerOf2; + if (alignmentA == alignmentB) { + if (definedAtomA->merge() == DefinedAtom::mergeAsTentative) + return false; + if (definedAtomB->merge() == DefinedAtom::mergeAsTentative) + return true; + } + return alignmentA < alignmentB; + }); + + // Set the fileOffset, and the appropriate size of the section + for (auto &ai : this->_atoms) { + const DefinedAtom *definedAtom = cast<DefinedAtom>(ai->_atom); + DefinedAtom::Alignment atomAlign = definedAtom->alignment(); + uint64_t fOffset = this->alignOffset(this->fileSize(), atomAlign); + uint64_t mOffset = this->alignOffset(this->memSize(), atomAlign); + ai->_fileOffset = fOffset; + this->_fsize = fOffset + definedAtom->size(); + this->_msize = mOffset + definedAtom->size(); + } +} // finalize + +} // elf +} // lld + +#endif // LLD_READER_WRITER_ELF_HEXAGON_HEXAGON_SECTION_CHUNKS_H diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.cpp b/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.cpp new file mode 100644 index 0000000000000..9b10c2f160f41 --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.cpp @@ -0,0 +1,334 @@ +//===- lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.cpp --------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "HexagonExecutableWriter.h" +#include "HexagonDynamicLibraryWriter.h" +#include "HexagonLinkingContext.h" +#include "HexagonTargetHandler.h" + +using namespace lld; +using namespace elf; +using namespace llvm::ELF; + +using llvm::makeArrayRef; + +HexagonTargetHandler::HexagonTargetHandler(HexagonLinkingContext &context) + : _hexagonLinkingContext(context), + _hexagonRuntimeFile(new HexagonRuntimeFile<HexagonELFType>(context)), + _hexagonTargetLayout(new HexagonTargetLayout<HexagonELFType>(context)), + _hexagonRelocationHandler(new HexagonTargetRelocationHandler( + *_hexagonTargetLayout.get())) {} + +std::unique_ptr<Writer> HexagonTargetHandler::getWriter() { + switch (_hexagonLinkingContext.getOutputELFType()) { + case llvm::ELF::ET_EXEC: + return std::unique_ptr<Writer>( + new elf::HexagonExecutableWriter<HexagonELFType>( + _hexagonLinkingContext, *_hexagonTargetLayout.get())); + case llvm::ELF::ET_DYN: + return std::unique_ptr<Writer>( + new elf::HexagonDynamicLibraryWriter<HexagonELFType>( + _hexagonLinkingContext, *_hexagonTargetLayout.get())); + case llvm::ELF::ET_REL: + llvm_unreachable("TODO: support -r mode"); + default: + llvm_unreachable("unsupported output type"); + } +} + +using namespace llvm::ELF; + +// .got atom +const uint8_t hexagonGotAtomContent[4] = { 0 }; +// .got.plt atom (entry 0) +const uint8_t hexagonGotPlt0AtomContent[16] = { 0 }; +// .got.plt atom (all other entries) +const uint8_t hexagonGotPltAtomContent[4] = { 0 }; +// .plt (entry 0) +const uint8_t hexagonPlt0AtomContent[28] = { + 0x00, 0x40, 0x00, 0x00, // { immext (#0) + 0x1c, 0xc0, 0x49, 0x6a, // r28 = add (pc, ##GOT0@PCREL) } # address of GOT0 + 0x0e, 0x42, 0x9c, 0xe2, // { r14 -= add (r28, #16) # offset of GOTn from GOTa + 0x4f, 0x40, 0x9c, 0x91, // r15 = memw (r28 + #8) # object ID at GOT2 + 0x3c, 0xc0, 0x9c, 0x91, // r28 = memw (r28 + #4) }# dynamic link at GOT1 + 0x0e, 0x42, 0x0e, 0x8c, // { r14 = asr (r14, #2) # index of PLTn + 0x00, 0xc0, 0x9c, 0x52, // jumpr r28 } # call dynamic linker +}; + +// .plt (other entries) +const uint8_t hexagonPltAtomContent[16] = { + 0x00, 0x40, 0x00, 0x00, // { immext (#0) + 0x0e, 0xc0, 0x49, 0x6a, // r14 = add (pc, ##GOTn@PCREL) } # address of GOTn + 0x1c, 0xc0, 0x8e, 0x91, // r28 = memw (r14) # contents of GOTn + 0x00, 0xc0, 0x9c, 0x52, // jumpr r28 # call it +}; + +class HexagonGOTAtom : public GOTAtom { +public: + HexagonGOTAtom(const File &f) : GOTAtom(f, ".got") {} + + ArrayRef<uint8_t> rawContent() const override { + return makeArrayRef(hexagonGotAtomContent); + } + + Alignment alignment() const override { return Alignment(2); } +}; + +class HexagonGOTPLTAtom : public GOTAtom { +public: + HexagonGOTPLTAtom(const File &f) : GOTAtom(f, ".got.plt") {} + + ArrayRef<uint8_t> rawContent() const override { + return makeArrayRef(hexagonGotPltAtomContent); + } + + Alignment alignment() const override { return Alignment(2); } +}; + +class HexagonGOTPLT0Atom : public GOTAtom { +public: + HexagonGOTPLT0Atom(const File &f) : GOTAtom(f, ".got.plt") {} + + ArrayRef<uint8_t> rawContent() const override { + return makeArrayRef(hexagonGotPlt0AtomContent); + } + + Alignment alignment() const override { return Alignment(3); } +}; + +class HexagonPLT0Atom : public PLT0Atom { +public: + HexagonPLT0Atom(const File &f) : PLT0Atom(f) {} + + ArrayRef<uint8_t> rawContent() const override { + return makeArrayRef(hexagonPlt0AtomContent); + } +}; + +class HexagonPLTAtom : public PLTAtom { + +public: + HexagonPLTAtom(const File &f, StringRef secName) : PLTAtom(f, secName) {} + + ArrayRef<uint8_t> rawContent() const override { + return makeArrayRef(hexagonPltAtomContent); + } +}; + +class ELFPassFile : public SimpleFile { +public: + ELFPassFile(const ELFLinkingContext &eti) : SimpleFile("ELFPassFile") { + setOrdinal(eti.getNextOrdinalAndIncrement()); + } + + llvm::BumpPtrAllocator _alloc; +}; + +/// \brief Create GOT and PLT entries for relocations. Handles standard GOT/PLT +template <class Derived> class GOTPLTPass : public Pass { + /// \brief Handle a specific reference. + void handleReference(const DefinedAtom &atom, const Reference &ref) { + if (ref.kindNamespace() != Reference::KindNamespace::ELF) + return; + assert(ref.kindArch() == Reference::KindArch::Hexagon); + switch (ref.kindValue()) { + case R_HEX_PLT_B22_PCREL: + case R_HEX_B22_PCREL: + static_cast<Derived *>(this)->handlePLT32(ref); + break; + case R_HEX_GOT_LO16: + case R_HEX_GOT_HI16: + case R_HEX_GOT_32_6_X: + case R_HEX_GOT_16_X: + case R_HEX_GOT_11_X: + static_cast<Derived *>(this)->handleGOTREL(ref); + break; + } + } + +protected: + /// \brief Create a GOT entry containing 0. + const GOTAtom *getNullGOT() { + if (!_null) { + _null = new (_file._alloc) HexagonGOTPLTAtom(_file); +#ifndef NDEBUG + _null->_name = "__got_null"; +#endif + } + return _null; + } + +public: + GOTPLTPass(const ELFLinkingContext &ctx) + : _file(ctx), _null(nullptr), _PLT0(nullptr), _got0(nullptr) {} + + /// \brief Do the pass. + /// + /// The goal here is to first process each reference individually. Each call + /// to handleReference may modify the reference itself and/or create new + /// atoms which must be stored in one of the maps below. + /// + /// After all references are handled, the atoms created during that are all + /// added to mf. + void perform(std::unique_ptr<MutableFile> &mf) override { + // Process all references. + for (const auto &atom : mf->defined()) + for (const auto &ref : *atom) + handleReference(*atom, *ref); + + // Add all created atoms to the link. + uint64_t ordinal = 0; + if (_PLT0) { + _PLT0->setOrdinal(ordinal++); + mf->addAtom(*_PLT0); + } + for (auto &plt : _pltVector) { + plt->setOrdinal(ordinal++); + mf->addAtom(*plt); + } + if (_null) { + _null->setOrdinal(ordinal++); + mf->addAtom(*_null); + } + if (_got0) { + _got0->setOrdinal(ordinal++); + mf->addAtom(*_got0); + } + for (auto &got : _gotVector) { + got->setOrdinal(ordinal++); + mf->addAtom(*got); + } + } + +protected: + /// \brief Owner of all the Atoms created by this pass. + ELFPassFile _file; + + /// \brief Map Atoms to their GOT entries. + llvm::DenseMap<const Atom *, GOTAtom *> _gotMap; + + /// \brief Map Atoms to their PLT entries. + llvm::DenseMap<const Atom *, PLTAtom *> _pltMap; + + /// \brief the list of GOT/PLT atoms + std::vector<GOTAtom *> _gotVector; + std::vector<PLTAtom *> _pltVector; + + /// \brief GOT entry that is always 0. Used for undefined weaks. + GOTAtom *_null; + + /// \brief The got and plt entries for .PLT0. This is used to call into the + /// dynamic linker for symbol resolution. + /// @{ + PLT0Atom *_PLT0; + GOTAtom *_got0; + /// @} +}; + +class DynamicGOTPLTPass final : public GOTPLTPass<DynamicGOTPLTPass> { +public: + DynamicGOTPLTPass(const elf::HexagonLinkingContext &ctx) : GOTPLTPass(ctx) { + _got0 = new (_file._alloc) HexagonGOTPLT0Atom(_file); +#ifndef NDEBUG + _got0->_name = "__got0"; +#endif + } + + const PLT0Atom *getPLT0() { + if (_PLT0) + return _PLT0; + _PLT0 = new (_file._alloc) HexagonPLT0Atom(_file); + _PLT0->addReferenceELF_Hexagon(R_HEX_B32_PCREL_X, 0, _got0, 0); + _PLT0->addReferenceELF_Hexagon(R_HEX_6_PCREL_X, 4, _got0, 4); + DEBUG_WITH_TYPE("PLT", llvm::dbgs() << "[ PLT0/GOT0 ] " + << "Adding plt0/got0 \n"); + return _PLT0; + } + + const PLTAtom *getPLTEntry(const Atom *a) { + auto plt = _pltMap.find(a); + if (plt != _pltMap.end()) + return plt->second; + auto ga = new (_file._alloc) HexagonGOTPLTAtom(_file); + ga->addReferenceELF_Hexagon(R_HEX_JMP_SLOT, 0, a, 0); + auto pa = new (_file._alloc) HexagonPLTAtom(_file, ".plt"); + pa->addReferenceELF_Hexagon(R_HEX_B32_PCREL_X, 0, ga, 0); + pa->addReferenceELF_Hexagon(R_HEX_6_PCREL_X, 4, ga, 4); + + // Point the got entry to the PLT0 atom initially + ga->addReferenceELF_Hexagon(R_HEX_32, 0, getPLT0(), 0); +#ifndef NDEBUG + ga->_name = "__got_"; + ga->_name += a->name(); + pa->_name = "__plt_"; + pa->_name += a->name(); + DEBUG_WITH_TYPE("PLT", llvm::dbgs() << "[" << a->name() << "] " + << "Adding plt/got: " << pa->_name + << "/" << ga->_name << "\n"); +#endif + _gotMap[a] = ga; + _pltMap[a] = pa; + _gotVector.push_back(ga); + _pltVector.push_back(pa); + return pa; + } + + const GOTAtom *getGOTEntry(const Atom *a) { + auto got = _gotMap.find(a); + if (got != _gotMap.end()) + return got->second; + auto ga = new (_file._alloc) HexagonGOTAtom(_file); + ga->addReferenceELF_Hexagon(R_HEX_GLOB_DAT, 0, a, 0); + +#ifndef NDEBUG + ga->_name = "__got_"; + ga->_name += a->name(); + DEBUG_WITH_TYPE("GOT", llvm::dbgs() << "[" << a->name() << "] " + << "Adding got: " << ga->_name << "\n"); +#endif + _gotMap[a] = ga; + _gotVector.push_back(ga); + return ga; + } + + std::error_code handleGOTREL(const Reference &ref) { + // Turn this so that the target is set to the GOT entry + const_cast<Reference &>(ref).setTarget(getGOTEntry(ref.target())); + return std::error_code(); + } + + std::error_code handlePLT32(const Reference &ref) { + // Turn this into a PC32 to the PLT entry. + assert(ref.kindNamespace() == Reference::KindNamespace::ELF); + assert(ref.kindArch() == Reference::KindArch::Hexagon); + const_cast<Reference &>(ref).setKindValue(R_HEX_B22_PCREL); + const_cast<Reference &>(ref).setTarget(getPLTEntry(ref.target())); + return std::error_code(); + } +}; + +void elf::HexagonLinkingContext::addPasses(PassManager &pm) { + if (isDynamic()) + pm.add(llvm::make_unique<DynamicGOTPLTPass>(*this)); + ELFLinkingContext::addPasses(pm); +} + +void HexagonTargetHandler::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, + Reference::KindArch::Hexagon, kindStrings); +} + +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), + +const Registry::KindStrings HexagonTargetHandler::kindStrings[] = { +#include "llvm/Support/ELFRelocs/Hexagon.def" + LLD_KIND_STRING_END +}; + +#undef ELF_RELOC diff --git a/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h b/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h new file mode 100644 index 0000000000000..f4315f710ec7c --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h @@ -0,0 +1,143 @@ +//===- lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h ----------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef HEXAGON_TARGET_HANDLER_H +#define HEXAGON_TARGET_HANDLER_H + +#include "DefaultTargetHandler.h" +#include "HexagonELFReader.h" +#include "HexagonExecutableAtoms.h" +#include "HexagonRelocationHandler.h" +#include "HexagonSectionChunks.h" +#include "TargetLayout.h" + +namespace lld { +namespace elf { +class HexagonLinkingContext; + +/// \brief TargetLayout for Hexagon +template <class HexagonELFType> +class HexagonTargetLayout final : public TargetLayout<HexagonELFType> { +public: + enum HexagonSectionOrder { + ORDER_SDATA = 205 + }; + + HexagonTargetLayout(HexagonLinkingContext &hti) + : TargetLayout<HexagonELFType>(hti), _sdataSection(nullptr), + _gotSymAtom(nullptr), _cachedGotSymAtom(false) { + _sdataSection = new (_alloc) SDataSection<HexagonELFType>(hti); + } + + /// \brief Return the section order for a input section + virtual Layout::SectionOrder getSectionOrder( + StringRef name, int32_t contentType, int32_t contentPermissions) { + if ((contentType == DefinedAtom::typeDataFast) || + (contentType == DefinedAtom::typeZeroFillFast)) + return ORDER_SDATA; + + return DefaultLayout<HexagonELFType>::getSectionOrder(name, contentType, + contentPermissions); + } + + /// \brief Return the appropriate input section name. + virtual StringRef getInputSectionName(const DefinedAtom *da) const { + switch (da->contentType()) { + case DefinedAtom::typeDataFast: + case DefinedAtom::typeZeroFillFast: + return ".sdata"; + default: + break; + } + return DefaultLayout<HexagonELFType>::getInputSectionName(da); + } + + /// \brief Gets or creates a section. + virtual AtomSection<HexagonELFType> * + createSection(StringRef name, int32_t contentType, + DefinedAtom::ContentPermissions contentPermissions, + Layout::SectionOrder sectionOrder) { + if ((contentType == DefinedAtom::typeDataFast) || + (contentType == DefinedAtom::typeZeroFillFast)) + return _sdataSection; + return DefaultLayout<HexagonELFType>::createSection( + name, contentType, contentPermissions, sectionOrder); + } + + /// \brief get the segment type for the section thats defined by the target + virtual Layout::SegmentType + getSegmentType(Section<HexagonELFType> *section) const { + if (section->order() == ORDER_SDATA) + return PT_LOAD; + + return DefaultLayout<HexagonELFType>::getSegmentType(section); + } + + Section<HexagonELFType> *getSDataSection() const { + return _sdataSection; + } + + uint64_t getGOTSymAddr() { + if (!_cachedGotSymAtom) { + auto gotAtomIter = this->findAbsoluteAtom("_GLOBAL_OFFSET_TABLE_"); + _gotSymAtom = (*gotAtomIter); + _cachedGotSymAtom = true; + } + if (_gotSymAtom) + return _gotSymAtom->_virtualAddr; + return 0; + } + +private: + llvm::BumpPtrAllocator _alloc; + SDataSection<HexagonELFType> *_sdataSection; + AtomLayout *_gotSymAtom; + bool _cachedGotSymAtom; +}; + +/// \brief TargetHandler for Hexagon +class HexagonTargetHandler final : + public DefaultTargetHandler<HexagonELFType> { +public: + HexagonTargetHandler(HexagonLinkingContext &targetInfo); + + void registerRelocationNames(Registry ®istry) override; + + const HexagonTargetRelocationHandler &getRelocationHandler() const override { + return *(_hexagonRelocationHandler.get()); + } + + HexagonTargetLayout<HexagonELFType> &getTargetLayout() override { + return *(_hexagonTargetLayout.get()); + } + + std::unique_ptr<Reader> getObjReader() override { + return std::unique_ptr<Reader>( + new HexagonELFObjectReader(_hexagonLinkingContext)); + } + + std::unique_ptr<Reader> getDSOReader() override { + return std::unique_ptr<Reader>( + new HexagonELFDSOReader(_hexagonLinkingContext)); + } + + std::unique_ptr<Writer> getWriter() override; + +private: + llvm::BumpPtrAllocator _alloc; + static const Registry::KindStrings kindStrings[]; + HexagonLinkingContext &_hexagonLinkingContext; + std::unique_ptr<HexagonRuntimeFile<HexagonELFType> > _hexagonRuntimeFile; + std::unique_ptr<HexagonTargetLayout<HexagonELFType>> _hexagonTargetLayout; + std::unique_ptr<HexagonTargetRelocationHandler> _hexagonRelocationHandler; +}; +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Hexagon/Makefile b/lib/ReaderWriter/ELF/Hexagon/Makefile new file mode 100644 index 0000000000000..8d6f1a0a3b1ed --- /dev/null +++ b/lib/ReaderWriter/ELF/Hexagon/Makefile @@ -0,0 +1,16 @@ +##===- lld/lib/ReaderWriter/ELF/Hexagon/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 := lldHexagonELFTarget +USEDLIBS = lldCore.a + +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF/Hexagon -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/Layout.h b/lib/ReaderWriter/ELF/Layout.h new file mode 100644 index 0000000000000..826cf5035d59a --- /dev/null +++ b/lib/ReaderWriter/ELF/Layout.h @@ -0,0 +1,59 @@ +//===- lib/ReaderWriter/ELF/Layout.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_LAYOUT_H +#define LLD_READER_WRITER_ELF_LAYOUT_H + +#include "lld/Core/DefinedAtom.h" +#include "lld/ReaderWriter/AtomLayout.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/ErrorOr.h" + +namespace lld { +namespace elf { + +/// \brief The ELFLayout is an abstract class for managing the final layout for +/// the kind of binaries(Shared Libraries / Relocatables / Executables 0 +/// Each architecture (Hexagon, MIPS) would have a concrete +/// subclass derived from Layout for generating each binary thats +// needed by the lld linker +class Layout { +public: + typedef uint32_t SectionOrder; + typedef uint32_t SegmentType; + typedef uint32_t Flags; + +public: + /// Return the order the section would appear in the output file + virtual SectionOrder getSectionOrder(StringRef name, int32_t contentType, + int32_t contentPerm) = 0; + /// \brief Append the Atom to the layout and create appropriate sections. + /// \returns A reference to the atom layout or an error. The atom layout will + /// be updated as linking progresses. + virtual ErrorOr<const lld::AtomLayout *> addAtom(const Atom *atom) = 0; + /// find the Atom in the current layout + virtual const AtomLayout *findAtomLayoutByName(StringRef name) const = 0; + /// associates a section to a segment + virtual void assignSectionsToSegments() = 0; + /// associates a virtual address to the segment, section, and the atom + virtual void assignVirtualAddress() = 0; + +public: + Layout() {} + + virtual ~Layout() { } +}; +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Makefile b/lib/ReaderWriter/ELF/Makefile new file mode 100644 index 0000000000000..5791ecb9733d1 --- /dev/null +++ b/lib/ReaderWriter/ELF/Makefile @@ -0,0 +1,18 @@ +##===- lld/lib/ReaderWriter/ELF/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 := lldELF + +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF + +# these link against this lib +PARALLEL_DIRS := Hexagon X86 X86_64 Mips AArch64 ARM + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/Mips/CMakeLists.txt b/lib/ReaderWriter/ELF/Mips/CMakeLists.txt new file mode 100644 index 0000000000000..d982508b7ddcc --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/CMakeLists.txt @@ -0,0 +1,14 @@ +add_llvm_library(lldMipsELFTarget + MipsCtorsOrderPass.cpp + MipsELFFlagsMerger.cpp + MipsLinkingContext.cpp + MipsRelocationHandler.cpp + MipsRelocationPass.cpp + MipsTargetHandler.cpp + LINK_LIBS + lldELF + lldReaderWriter + lldCore + LLVMObject + LLVMSupport + ) diff --git a/lib/ReaderWriter/ELF/Mips/Makefile b/lib/ReaderWriter/ELF/Mips/Makefile new file mode 100644 index 0000000000000..0b2f4ff82279a --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/Makefile @@ -0,0 +1,15 @@ +##===- lld/lib/ReaderWriter/ELF/Mips/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 := lldMipsELFTarget +USEDLIBS = lldCore.a +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.cpp b/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.cpp new file mode 100644 index 0000000000000..8bf80257fc89f --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.cpp @@ -0,0 +1,73 @@ +//===- lib/ReaderWriter/ELF/Mips/Mips/CtorsOrderPass.cpp ------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MipsCtorsOrderPass.h" +#include <algorithm> +#include <climits> + +using namespace lld; +using namespace lld::elf; + +static bool matchCrtObjName(StringRef objName, StringRef objPath) { + if (!objPath.endswith(".o")) + return false; + + // check *<objName> case + objPath = objPath.drop_back(2); + if (objPath.endswith(objName)) + return true; + + // check *<objName>? case + return !objPath.empty() && objPath.drop_back(1).endswith(objName); +} + +static int32_t getSectionPriority(StringRef path, StringRef sectionName) { + // Arrange .ctors/.dtors sections in the following order: + // .ctors from crtbegin.o or crtbegin?.o + // .ctors from regular object files + // .ctors.* (sorted) from regular object files + // .ctors from crtend.o or crtend?.o + + if (matchCrtObjName("crtbegin", path)) + return std::numeric_limits<int32_t>::min(); + if (matchCrtObjName("crtend", path)) + return std::numeric_limits<int32_t>::max(); + + StringRef num = sectionName.drop_front().rsplit('.').second; + + int32_t priority = std::numeric_limits<int32_t>::min() + 1; + if (!num.empty()) + num.getAsInteger(10, priority); + + return priority; +} + +void MipsCtorsOrderPass::perform(std::unique_ptr<MutableFile> &f) { + auto definedAtoms = f->definedAtoms(); + + auto last = std::stable_partition(definedAtoms.begin(), definedAtoms.end(), + [](const DefinedAtom *atom) { + if (atom->sectionChoice() != DefinedAtom::sectionCustomRequired) + return false; + + StringRef name = atom->customSectionName(); + return name.startswith(".ctors") || name.startswith(".dtors"); + }); + + std::stable_sort(definedAtoms.begin(), last, + [](const DefinedAtom *left, const DefinedAtom *right) { + StringRef leftSec = left->customSectionName(); + StringRef rightSec = right->customSectionName(); + + int32_t leftPriority = getSectionPriority(left->file().path(), leftSec); + int32_t rightPriority = getSectionPriority(right->file().path(), rightSec); + + return leftPriority < rightPriority; + }); +} diff --git a/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.h b/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.h new file mode 100644 index 0000000000000..eeb1a194f9c77 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.h @@ -0,0 +1,25 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsCtorsOrderPass.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_MIPS_MIPS_CTORS_ORDER_PASS_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_CTORS_ORDER_PASS_H + +#include "lld/Core/Pass.h" + +namespace lld { +namespace elf { +/// \brief This pass sorts atoms in .{ctors,dtors}.<priority> sections. +class MipsCtorsOrderPass : public Pass { +public: + void perform(std::unique_ptr<MutableFile> &mergedFile) override; +}; +} +} + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsDynamicLibraryWriter.h b/lib/ReaderWriter/ELF/Mips/MipsDynamicLibraryWriter.h new file mode 100644 index 0000000000000..30b5b0ba6dae9 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsDynamicLibraryWriter.h @@ -0,0 +1,101 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsDynamicLibraryWriter.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_MIPS_MIPS_DYNAMIC_LIBRARY_WRITER_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_DYNAMIC_LIBRARY_WRITER_H + +#include "DynamicLibraryWriter.h" +#include "MipsDynamicTable.h" +#include "MipsELFWriters.h" +#include "MipsLinkingContext.h" + +namespace lld { +namespace elf { + +template <typename ELFT> class MipsSymbolTable; +template <typename ELFT> class MipsDynamicSymbolTable; +template <typename ELFT> class MipsTargetLayout; + +template <class ELFT> +class MipsDynamicLibraryWriter : public DynamicLibraryWriter<ELFT> { +public: + MipsDynamicLibraryWriter(MipsLinkingContext &ctx, + MipsTargetLayout<ELFT> &layout); + +protected: + // Add any runtime files and their atoms to the output + bool createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; + + void finalizeDefaultAtomValues() override; + + std::error_code setELFHeader() override { + DynamicLibraryWriter<ELFT>::setELFHeader(); + _writeHelper.setELFHeader(*this->_elfHeader); + return std::error_code(); + } + + unique_bump_ptr<SymbolTable<ELFT>> createSymbolTable() override; + unique_bump_ptr<DynamicTable<ELFT>> createDynamicTable() override; + + unique_bump_ptr<DynamicSymbolTable<ELFT>> + createDynamicSymbolTable() override; + +private: + MipsELFWriter<ELFT> _writeHelper; + MipsTargetLayout<ELFT> &_mipsTargetLayout; +}; + +template <class ELFT> +MipsDynamicLibraryWriter<ELFT>::MipsDynamicLibraryWriter( + MipsLinkingContext &ctx, MipsTargetLayout<ELFT> &layout) + : DynamicLibraryWriter<ELFT>(ctx, layout), _writeHelper(ctx, layout), + _mipsTargetLayout(layout) {} + +template <class ELFT> +bool MipsDynamicLibraryWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + DynamicLibraryWriter<ELFT>::createImplicitFiles(result); + result.push_back(std::move(_writeHelper.createRuntimeFile())); + return true; +} + +template <class ELFT> +void MipsDynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues() { + // Finalize the atom values that are part of the parent. + DynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues(); + _writeHelper.finalizeMipsRuntimeAtomValues(); +} + +template <class ELFT> +unique_bump_ptr<SymbolTable<ELFT>> + MipsDynamicLibraryWriter<ELFT>::createSymbolTable() { + return unique_bump_ptr<SymbolTable<ELFT>>(new ( + this->_alloc) MipsSymbolTable<ELFT>(this->_context)); +} + +/// \brief create dynamic table +template <class ELFT> +unique_bump_ptr<DynamicTable<ELFT>> + MipsDynamicLibraryWriter<ELFT>::createDynamicTable() { + return unique_bump_ptr<DynamicTable<ELFT>>(new ( + this->_alloc) MipsDynamicTable<ELFT>(this->_context, _mipsTargetLayout)); +} + +/// \brief create dynamic symbol table +template <class ELFT> +unique_bump_ptr<DynamicSymbolTable<ELFT>> + MipsDynamicLibraryWriter<ELFT>::createDynamicSymbolTable() { + return unique_bump_ptr<DynamicSymbolTable<ELFT>>( + new (this->_alloc) MipsDynamicSymbolTable<ELFT>( + this->_context, _mipsTargetLayout)); +} + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsDynamicTable.h b/lib/ReaderWriter/ELF/Mips/MipsDynamicTable.h new file mode 100644 index 0000000000000..2b9562f42b57d --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsDynamicTable.h @@ -0,0 +1,115 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsDynamicTable.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_MIPS_MIPS_DYNAMIC_TABLE_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_DYNAMIC_TABLE_H + +#include "DefaultLayout.h" +#include "SectionChunks.h" + +namespace lld { +namespace elf { + +template <class ELFType> class MipsTargetLayout; + +template <class MipsELFType> +class MipsDynamicTable : public DynamicTable<MipsELFType> { +public: + MipsDynamicTable(const ELFLinkingContext &ctx, + MipsTargetLayout<MipsELFType> &layout) + : DynamicTable<MipsELFType>(ctx, layout, ".dynamic", + DefaultLayout<MipsELFType>::ORDER_DYNAMIC), + _mipsTargetLayout(layout) {} + + void createDefaultEntries() override { + DynamicTable<MipsELFType>::createDefaultEntries(); + + typename DynamicTable<MipsELFType>::Elf_Dyn dyn; + + // Version id for the Runtime Linker Interface. + dyn.d_un.d_val = 1; + dyn.d_tag = DT_MIPS_RLD_VERSION; + this->addEntry(dyn); + + // MIPS flags. + dyn.d_un.d_val = RHF_NOTPOT; + dyn.d_tag = DT_MIPS_FLAGS; + this->addEntry(dyn); + + // The base address of the segment. + dyn.d_un.d_ptr = 0; + dyn.d_tag = DT_MIPS_BASE_ADDRESS; + _dt_baseaddr = this->addEntry(dyn); + + // Number of local global offset table entries. + dyn.d_un.d_val = 0; + dyn.d_tag = DT_MIPS_LOCAL_GOTNO; + _dt_localgot = this->addEntry(dyn); + + // Number of entries in the .dynsym section. + dyn.d_un.d_val = 0; + dyn.d_tag = DT_MIPS_SYMTABNO; + _dt_symtabno = this->addEntry(dyn); + + // The index of the first dynamic symbol table entry that corresponds + // to an entry in the global offset table. + dyn.d_un.d_val = 0; + dyn.d_tag = DT_MIPS_GOTSYM; + _dt_gotsym = this->addEntry(dyn); + + // Address of the .got section. + dyn.d_un.d_val = 0; + dyn.d_tag = DT_PLTGOT; + _dt_pltgot = this->addEntry(dyn); + } + + void updateDynamicTable() override { + DynamicTable<MipsELFType>::updateDynamicTable(); + + // Assign the minimum segment address to the DT_MIPS_BASE_ADDRESS tag. + auto baseAddr = std::numeric_limits<uint64_t>::max(); + for (auto si : _mipsTargetLayout.segments()) + if (si->segmentType() != llvm::ELF::PT_NULL) + baseAddr = std::min(baseAddr, si->virtualAddr()); + this->_entries[_dt_baseaddr].d_un.d_val = baseAddr; + + auto &got = _mipsTargetLayout.getGOTSection(); + + this->_entries[_dt_symtabno].d_un.d_val = this->getSymbolTable()->size(); + this->_entries[_dt_gotsym].d_un.d_val = + this-> getSymbolTable()->size() - got.getGlobalCount(); + this->_entries[_dt_localgot].d_un.d_val = got.getLocalCount(); + this->_entries[_dt_pltgot].d_un.d_ptr = + _mipsTargetLayout.findOutputSection(".got")->virtualAddr(); + } + + int64_t getGotPltTag() override { return DT_MIPS_PLTGOT; } + +protected: + /// \brief Adjust the symbol's value for microMIPS code. + uint64_t getAtomVirtualAddress(const AtomLayout *al) const override { + if (const auto *da = dyn_cast<DefinedAtom>(al->_atom)) + if (da->codeModel() == DefinedAtom::codeMipsMicro || + da->codeModel() == DefinedAtom::codeMipsMicroPIC) + return al->_virtualAddr | 1; + return al->_virtualAddr; + } + +private: + std::size_t _dt_symtabno; + std::size_t _dt_localgot; + std::size_t _dt_gotsym; + std::size_t _dt_pltgot; + std::size_t _dt_baseaddr; + MipsTargetLayout<MipsELFType> &_mipsTargetLayout; +}; + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFFile.h b/lib/ReaderWriter/ELF/Mips/MipsELFFile.h new file mode 100644 index 0000000000000..7381c7e977bf2 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsELFFile.h @@ -0,0 +1,331 @@ +//===- lib/ReaderWriter/ELF/MipsELFFile.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_MIPS_MIPS_ELF_FILE_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_ELF_FILE_H + +#include "ELFReader.h" +#include "MipsLinkingContext.h" +#include "MipsRelocationHandler.h" + +namespace llvm { +namespace object { + +template <class ELFT> +struct Elf_RegInfo; + +template <llvm::support::endianness TargetEndianness, std::size_t MaxAlign> +struct Elf_RegInfo<ELFType<TargetEndianness, MaxAlign, false>> { + LLVM_ELF_IMPORT_TYPES(TargetEndianness, MaxAlign, false) + Elf_Word ri_gprmask; // bit-mask of used general registers + Elf_Word ri_cprmask[4]; // bit-mask of used co-processor registers + Elf_Addr ri_gp_value; // gp register value +}; + +template <llvm::support::endianness TargetEndianness, std::size_t MaxAlign> +struct Elf_RegInfo<ELFType<TargetEndianness, MaxAlign, true>> { + LLVM_ELF_IMPORT_TYPES(TargetEndianness, MaxAlign, true) + Elf_Word ri_gprmask; // bit-mask of used general registers + Elf_Word ri_pad; // unused padding field + Elf_Word ri_cprmask[4]; // bit-mask of used co-processor registers + Elf_Addr ri_gp_value; // gp register value +}; + +template <class ELFT> struct Elf_Mips_Options { + LLVM_ELF_IMPORT_TYPES(ELFT::TargetEndianness, ELFT::MaxAlignment, + ELFT::Is64Bits) + uint8_t kind; // Determines interpretation of variable part of descriptor + uint8_t size; // Byte size of descriptor, including this header + Elf_Half section; // Section header index of section affected, + // or 0 for global options + Elf_Word info; // Kind-specific information +}; + +} // end namespace object. +} // end namespace llvm. + +namespace lld { +namespace elf { + +template <class ELFT> class MipsELFFile; + +template <class ELFT> +class MipsELFDefinedAtom : public ELFDefinedAtom<ELFT> { + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + +public: + MipsELFDefinedAtom(const MipsELFFile<ELFT> &file, StringRef symbolName, + StringRef sectionName, const Elf_Sym *symbol, + const Elf_Shdr *section, ArrayRef<uint8_t> contentData, + unsigned int referenceStart, unsigned int referenceEnd, + std::vector<ELFReference<ELFT> *> &referenceList) + : ELFDefinedAtom<ELFT>(file, symbolName, sectionName, symbol, section, + contentData, referenceStart, referenceEnd, + referenceList) {} + + const MipsELFFile<ELFT>& file() const override { + return static_cast<const MipsELFFile<ELFT> &>(this->_owningFile); + } + + DefinedAtom::CodeModel codeModel() const override { + switch (this->_symbol->st_other & llvm::ELF::STO_MIPS_MIPS16) { + case llvm::ELF::STO_MIPS_MIPS16: + return DefinedAtom::codeMips16; + case llvm::ELF::STO_MIPS_PIC: + return DefinedAtom::codeMipsPIC; + case llvm::ELF::STO_MIPS_MICROMIPS: + return DefinedAtom::codeMipsMicro; + case llvm::ELF::STO_MIPS_MICROMIPS | llvm::ELF::STO_MIPS_PIC: + return DefinedAtom::codeMipsMicroPIC; + default: + return DefinedAtom::codeNA; + } + } +}; + +template <class ELFT> class MipsELFReference : public ELFReference<ELFT> { + typedef llvm::object::Elf_Rel_Impl<ELFT, false> Elf_Rel; + typedef llvm::object::Elf_Rel_Impl<ELFT, true> Elf_Rela; + + static const bool _isMips64EL = + ELFT::Is64Bits && ELFT::TargetEndianness == llvm::support::little; + +public: + MipsELFReference(uint64_t symValue, const Elf_Rela &rel) + : ELFReference<ELFT>( + &rel, rel.r_offset - symValue, Reference::KindArch::Mips, + rel.getType(_isMips64EL) & 0xff, rel.getSymbol(_isMips64EL)), + _tag(uint32_t(rel.getType(_isMips64EL)) >> 8) {} + + MipsELFReference(uint64_t symValue, const Elf_Rel &rel) + : ELFReference<ELFT>(rel.r_offset - symValue, Reference::KindArch::Mips, + rel.getType(_isMips64EL) & 0xff, + rel.getSymbol(_isMips64EL)), + _tag(uint32_t(rel.getType(_isMips64EL)) >> 8) {} + + uint32_t tag() const override { return _tag; } + void setTag(uint32_t tag) { _tag = tag; } + +private: + uint32_t _tag; +}; + +template <class ELFT> class MipsELFFile : public ELFFile<ELFT> { +public: + MipsELFFile(std::unique_ptr<MemoryBuffer> mb, MipsLinkingContext &ctx) + : ELFFile<ELFT>(std::move(mb), ctx) {} + + static ErrorOr<std::unique_ptr<MipsELFFile>> + create(std::unique_ptr<MemoryBuffer> mb, MipsLinkingContext &ctx) { + return std::unique_ptr<MipsELFFile<ELFT>>( + new MipsELFFile<ELFT>(std::move(mb), ctx)); + } + + bool isPIC() const { + return this->_objFile->getHeader()->e_flags & llvm::ELF::EF_MIPS_PIC; + } + + /// \brief gp register value stored in the .reginfo section. + int64_t getGP0() const { return _gp0 ? *_gp0 : 0; } + + /// \brief .tdata section address plus fixed offset. + uint64_t getTPOffset() const { return *_tpOff; } + uint64_t getDTPOffset() const { return *_dtpOff; } + +protected: + std::error_code doParse() override { + if (std::error_code ec = ELFFile<ELFT>::doParse()) + return ec; + // Retrieve some auxiliary data like GP value, TLS section address etc + // from the object file. + return readAuxData(); + } + +private: + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + typedef llvm::object::Elf_Shdr_Impl<ELFT> Elf_Shdr; + typedef llvm::object::Elf_Rel_Impl<ELFT, false> Elf_Rel; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Rel_Iter Elf_Rel_Iter; + typedef typename llvm::object::ELFFile<ELFT>::Elf_Rela_Iter Elf_Rela_Iter; + + enum { TP_OFFSET = 0x7000, DTP_OFFSET = 0x8000 }; + + static const bool _isMips64EL = + ELFT::Is64Bits && ELFT::TargetEndianness == llvm::support::little; + + llvm::Optional<int64_t> _gp0; + llvm::Optional<uint64_t> _tpOff; + llvm::Optional<uint64_t> _dtpOff; + + ErrorOr<ELFDefinedAtom<ELFT> *> handleDefinedSymbol( + StringRef symName, StringRef sectionName, const Elf_Sym *sym, + const Elf_Shdr *sectionHdr, ArrayRef<uint8_t> contentData, + unsigned int referenceStart, unsigned int referenceEnd, + std::vector<ELFReference<ELFT> *> &referenceList) override { + return new (this->_readerStorage) MipsELFDefinedAtom<ELFT>( + *this, symName, sectionName, sym, sectionHdr, contentData, + referenceStart, referenceEnd, referenceList); + } + + const Elf_Shdr *findSectionByType(uint64_t type) { + for (const Elf_Shdr §ion : this->_objFile->sections()) + if (section.sh_type == type) + return §ion; + return nullptr; + } + + const Elf_Shdr *findSectionByFlags(uint64_t flags) { + for (const Elf_Shdr §ion : this->_objFile->sections()) + if (section.sh_flags & flags) + return §ion; + return nullptr; + } + + std::error_code readAuxData() { + using namespace llvm::ELF; + if (const Elf_Shdr *sec = findSectionByFlags(SHF_TLS)) { + _tpOff = sec->sh_addr + TP_OFFSET; + _dtpOff = sec->sh_addr + DTP_OFFSET; + } + + typedef llvm::object::Elf_RegInfo<ELFT> Elf_RegInfo; + typedef llvm::object::Elf_Mips_Options<ELFT> Elf_Mips_Options; + + if (const Elf_Shdr *sec = findSectionByType(SHT_MIPS_OPTIONS)) { + auto contents = this->getSectionContents(sec); + if (std::error_code ec = contents.getError()) + return ec; + + ArrayRef<uint8_t> raw = contents.get(); + while (!raw.empty()) { + if (raw.size() < sizeof(Elf_Mips_Options)) + return make_dynamic_error_code( + StringRef("Invalid size of MIPS_OPTIONS section")); + + const auto *opt = reinterpret_cast<const Elf_Mips_Options *>(raw.data()); + if (opt->kind == ODK_REGINFO) { + _gp0 = reinterpret_cast<const Elf_RegInfo *>(opt + 1)->ri_gp_value; + break; + } + raw = raw.slice(opt->size); + } + } else if (const Elf_Shdr *sec = findSectionByType(SHT_MIPS_REGINFO)) { + auto contents = this->getSectionContents(sec); + if (std::error_code ec = contents.getError()) + return ec; + + ArrayRef<uint8_t> raw = contents.get(); + if (raw.size() != sizeof(Elf_RegInfo)) + return make_dynamic_error_code( + StringRef("Invalid size of MIPS_REGINFO section")); + + _gp0 = reinterpret_cast<const Elf_RegInfo *>(raw.data())->ri_gp_value; + } + return std::error_code(); + } + + void createRelocationReferences(const Elf_Sym *symbol, + ArrayRef<uint8_t> content, + range<Elf_Rela_Iter> rels) override { + const auto value = this->getSymbolValue(symbol); + for (const auto &rel : rels) { + if (rel.r_offset < value || value + content.size() <= rel.r_offset) + continue; + auto r = new (this->_readerStorage) MipsELFReference<ELFT>(value, rel); + this->addReferenceToSymbol(r, symbol); + this->_references.push_back(r); + } + } + + void createRelocationReferences(const Elf_Sym *symbol, + ArrayRef<uint8_t> symContent, + ArrayRef<uint8_t> secContent, + range<Elf_Rel_Iter> rels) override { + const auto value = this->getSymbolValue(symbol); + for (Elf_Rel_Iter rit = rels.begin(), eit = rels.end(); rit != eit; ++rit) { + if (rit->r_offset < value || value + symContent.size() <= rit->r_offset) + continue; + + auto r = new (this->_readerStorage) MipsELFReference<ELFT>(value, *rit); + this->addReferenceToSymbol(r, symbol); + this->_references.push_back(r); + + auto addend = readAddend(*rit, secContent); + auto pairRelType = getPairRelocation(*rit); + if (pairRelType != llvm::ELF::R_MIPS_NONE) { + addend <<= 16; + auto mit = findMatchingRelocation(pairRelType, rit, eit); + if (mit != eit) + addend += int16_t(readAddend(*mit, secContent)); + else + // FIXME (simon): Show detailed warning. + llvm::errs() << "lld warning: cannot matching LO16 relocation\n"; + } + this->_references.back()->setAddend(addend); + } + } + + Reference::Addend readAddend(const Elf_Rel &ri, + const ArrayRef<uint8_t> content) const { + const auto &rh = + this->_ctx.template getTargetHandler<ELFT>().getRelocationHandler(); + return static_cast<const MipsRelocationHandler &>(rh) + .readAddend(getPrimaryType(ri), content.data() + ri.r_offset); + } + + uint32_t getPairRelocation(const Elf_Rel &rel) const { + switch (getPrimaryType(rel)) { + case llvm::ELF::R_MIPS_HI16: + return llvm::ELF::R_MIPS_LO16; + case llvm::ELF::R_MIPS_PCHI16: + return llvm::ELF::R_MIPS_PCLO16; + case llvm::ELF::R_MIPS_GOT16: + if (isLocalBinding(rel)) + return llvm::ELF::R_MIPS_LO16; + break; + case llvm::ELF::R_MICROMIPS_HI16: + return llvm::ELF::R_MICROMIPS_LO16; + case llvm::ELF::R_MICROMIPS_GOT16: + if (isLocalBinding(rel)) + return llvm::ELF::R_MICROMIPS_LO16; + break; + default: + // Nothing to do. + break; + } + return llvm::ELF::R_MIPS_NONE; + } + + Elf_Rel_Iter findMatchingRelocation(uint32_t pairRelType, Elf_Rel_Iter rit, + Elf_Rel_Iter eit) const { + return std::find_if(rit, eit, [&](const Elf_Rel &rel) { + return getPrimaryType(rel) == pairRelType && + rel.getSymbol(_isMips64EL) == rit->getSymbol(_isMips64EL); + }); + } + + static uint8_t getPrimaryType(const Elf_Rel &rel) { + return rel.getType(_isMips64EL) & 0xff; + } + bool isLocalBinding(const Elf_Rel &rel) const { + return this->_objFile->getSymbol(rel.getSymbol(_isMips64EL)) + ->getBinding() == llvm::ELF::STB_LOCAL; + } +}; + +template <class ELFT> class MipsDynamicFile : public DynamicFile<ELFT> { +public: + MipsDynamicFile(const MipsLinkingContext &context, StringRef name) + : DynamicFile<ELFT>(context, name) {} +}; + +} // elf +} // lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.cpp b/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.cpp new file mode 100644 index 0000000000000..0ef2c70b81564 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.cpp @@ -0,0 +1,149 @@ +//===- lib/ReaderWriter/ELF/MipsELFFlagsMerger.cpp ------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MipsELFFlagsMerger.h" +#include "lld/Core/Error.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/raw_ostream.h" + +using namespace lld; +using namespace lld::elf; +using namespace llvm::ELF; + +struct MipsISATreeEdge { + unsigned child; + unsigned parent; +}; + +static MipsISATreeEdge isaTree[] = { + // MIPS32R6 and MIPS64R6 are not compatible with other extensions + + // MIPS64 extensions. + {EF_MIPS_ARCH_64R2, EF_MIPS_ARCH_64}, + // MIPS V extensions. + {EF_MIPS_ARCH_64, EF_MIPS_ARCH_5}, + // MIPS IV extensions. + {EF_MIPS_ARCH_5, EF_MIPS_ARCH_4}, + // MIPS III extensions. + {EF_MIPS_ARCH_4, EF_MIPS_ARCH_3}, + // MIPS32 extensions. + {EF_MIPS_ARCH_32R2, EF_MIPS_ARCH_32}, + // MIPS II extensions. + {EF_MIPS_ARCH_3, EF_MIPS_ARCH_2}, + {EF_MIPS_ARCH_32, EF_MIPS_ARCH_2}, + // MIPS I extensions. + {EF_MIPS_ARCH_2, EF_MIPS_ARCH_1}, +}; + +static bool matchMipsISA(unsigned base, unsigned ext) { + if (base == ext) + return true; + if (base == EF_MIPS_ARCH_32 && matchMipsISA(EF_MIPS_ARCH_64, ext)) + return true; + if (base == EF_MIPS_ARCH_32R2 && matchMipsISA(EF_MIPS_ARCH_64R2, ext)) + return true; + for (const auto &edge : isaTree) { + if (ext == edge.child) { + ext = edge.parent; + if (ext == base) + return true; + } + } + return false; +} + +MipsELFFlagsMerger::MipsELFFlagsMerger(bool is64Bits) + : _is64Bit(is64Bits), _flags(0) {} + +uint32_t MipsELFFlagsMerger::getMergedELFFlags() const { return _flags; } + +std::error_code MipsELFFlagsMerger::merge(uint8_t newClass, uint32_t newFlags) { + // Check bitness. + if (_is64Bit != (newClass == ELFCLASS64)) + return make_dynamic_error_code( + Twine("Bitness is incompatible with that of the selected target")); + + // We support two ABI: O32 and N64. The last one does not have + // the corresponding ELF flag. + uint32_t inAbi = newFlags & EF_MIPS_ABI; + uint32_t supportedAbi = _is64Bit ? 0 : uint32_t(EF_MIPS_ABI_O32); + if (inAbi != supportedAbi) + return make_dynamic_error_code(Twine("Unsupported ABI")); + + // ... and reduced set of architectures ... + uint32_t newArch = newFlags & EF_MIPS_ARCH; + switch (newArch) { + case EF_MIPS_ARCH_1: + case EF_MIPS_ARCH_2: + case EF_MIPS_ARCH_3: + case EF_MIPS_ARCH_4: + case EF_MIPS_ARCH_5: + case EF_MIPS_ARCH_32: + case EF_MIPS_ARCH_64: + case EF_MIPS_ARCH_32R2: + case EF_MIPS_ARCH_64R2: + case EF_MIPS_ARCH_32R6: + case EF_MIPS_ARCH_64R6: + break; + default: + return make_dynamic_error_code(Twine("Unsupported instruction set")); + } + + // ... and still do not support MIPS-16 extension. + if (newFlags & EF_MIPS_ARCH_ASE_M16) + return make_dynamic_error_code(Twine("Unsupported extension: MIPS16")); + + // PIC code is inherently CPIC and may not set CPIC flag explicitly. + // Ensure that this flag will exist in the linked file. + if (newFlags & EF_MIPS_PIC) + newFlags |= EF_MIPS_CPIC; + + std::lock_guard<std::mutex> lock(_mutex); + + // If the old set of flags is empty, use the new one as a result. + if (!_flags) { + _flags = newFlags; + return std::error_code(); + } + + // Check PIC / CPIC flags compatibility. + uint32_t newPic = newFlags & (EF_MIPS_PIC | EF_MIPS_CPIC); + uint32_t oldPic = _flags & (EF_MIPS_PIC | EF_MIPS_CPIC); + + if ((newPic != 0) != (oldPic != 0)) + llvm::errs() << "lld warning: linking abicalls and non-abicalls files\n"; + + if (!(newPic & EF_MIPS_PIC)) + _flags &= ~EF_MIPS_PIC; + if (newPic) + _flags |= EF_MIPS_CPIC; + + // Check mixing -mnan=2008 / -mnan=legacy modules. + if ((newFlags & EF_MIPS_NAN2008) != (_flags & EF_MIPS_NAN2008)) + return make_dynamic_error_code( + Twine("Linking -mnan=2008 and -mnan=legacy modules")); + + // Check ISA compatibility and update the extension flag. + uint32_t oldArch = _flags & EF_MIPS_ARCH; + if (!matchMipsISA(newArch, oldArch)) { + if (!matchMipsISA(oldArch, newArch)) + return make_dynamic_error_code( + Twine("Linking modules with incompatible ISA")); + _flags &= ~EF_MIPS_ARCH; + _flags |= newArch; + } + + _flags |= newFlags & EF_MIPS_NOREORDER; + _flags |= newFlags & EF_MIPS_MICROMIPS; + _flags |= newFlags & EF_MIPS_NAN2008; + _flags |= newFlags & EF_MIPS_32BITMODE; + + return std::error_code(); +} diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.h b/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.h new file mode 100644 index 0000000000000..6ade86f0163cc --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsELFFlagsMerger.h @@ -0,0 +1,36 @@ +//===- lib/ReaderWriter/ELF/MipsELFFlagsMerger.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_MIPS_MIPS_ELF_FLAGS_MERGER_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_ELF_FLAGS_MERGER_H + +#include <mutex> +#include <system_error> + +namespace lld { +namespace elf { + +class MipsELFFlagsMerger { +public: + MipsELFFlagsMerger(bool is64Bits); + + uint32_t getMergedELFFlags() const; + + /// \brief Merge saved ELF header flags and the new set of flags. + std::error_code merge(uint8_t newClass, uint32_t newFlags); + +private: + const bool _is64Bit; + std::mutex _mutex; + uint32_t _flags; +}; + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFReader.h b/lib/ReaderWriter/ELF/Mips/MipsELFReader.h new file mode 100644 index 0000000000000..8b325b38bb522 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsELFReader.h @@ -0,0 +1,93 @@ +//===- lib/ReaderWriter/ELF/MipsELFReader.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_MIPS_MIPS_ELF_READER_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_ELF_READER_H + +#include "ELFReader.h" +#include "MipsELFFile.h" +#include "MipsELFFlagsMerger.h" +#include "MipsLinkingContext.h" + +namespace lld { +namespace elf { + +struct MipsELFFileCreateTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::File>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + MipsLinkingContext &ctx) { + return lld::elf::MipsELFFile<ELFT>::create(std::move(mb), ctx); + } +}; + +struct MipsDynamicFileCreateELFTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::SharedLibraryFile>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + MipsLinkingContext &ctx) { + return lld::elf::MipsDynamicFile<ELFT>::create(std::move(mb), ctx); + } +}; + +template <class ELFT> +class MipsELFObjectReader + : public ELFObjectReader<ELFT, MipsELFFileCreateTraits, + MipsLinkingContext> { + typedef ELFObjectReader<ELFT, MipsELFFileCreateTraits, MipsLinkingContext> + BaseReaderType; + +public: + MipsELFObjectReader(MipsLinkingContext &ctx) + : BaseReaderType(ctx, llvm::ELF::EM_MIPS), + _flagMerger(ctx.getELFFlagsMerger()) {} + + std::error_code + loadFile(std::unique_ptr<MemoryBuffer> mb, const Registry ®istry, + std::vector<std::unique_ptr<File>> &result) const override { + auto &hdr = *this->elfHeader(*mb); + if (std::error_code ec = _flagMerger.merge(hdr.getFileClass(), hdr.e_flags)) + return ec; + return BaseReaderType::loadFile(std::move(mb), registry, result); + } + +private: + MipsELFFlagsMerger &_flagMerger; +}; + +template <class ELFT> +class MipsELFDSOReader + : public ELFDSOReader<ELFT, MipsDynamicFileCreateELFTraits, + MipsLinkingContext> { + typedef ELFDSOReader<ELFT, MipsDynamicFileCreateELFTraits, MipsLinkingContext> + BaseReaderType; + +public: + MipsELFDSOReader(MipsLinkingContext &ctx) + : BaseReaderType(ctx, llvm::ELF::EM_MIPS), + _flagMerger(ctx.getELFFlagsMerger()) {} + + std::error_code + loadFile(std::unique_ptr<MemoryBuffer> mb, const Registry ®istry, + std::vector<std::unique_ptr<File>> &result) const override { + auto &hdr = *this->elfHeader(*mb); + if (std::error_code ec = _flagMerger.merge(hdr.getFileClass(), hdr.e_flags)) + return ec; + return BaseReaderType::loadFile(std::move(mb), registry, result); + } + +private: + MipsELFFlagsMerger &_flagMerger; +}; + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsELFWriters.h b/lib/ReaderWriter/ELF/Mips/MipsELFWriters.h new file mode 100644 index 0000000000000..d94dd757a0f30 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsELFWriters.h @@ -0,0 +1,82 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsELFWriters.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_MIPS_MIPS_ELF_WRITERS_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_ELF_WRITERS_H + +#include "MipsLinkingContext.h" +#include "OutputELFWriter.h" + +namespace lld { +namespace elf { + +template <class ELFT> class MipsRuntimeFile; + +template <class ELFT> class MipsTargetLayout; + +template <typename ELFT> class MipsELFWriter { +public: + MipsELFWriter(MipsLinkingContext &ctx, MipsTargetLayout<ELFT> &targetLayout) + : _ctx(ctx), _targetLayout(targetLayout) {} + + void setELFHeader(ELFHeader<ELFT> &elfHeader) { + elfHeader.e_version(1); + elfHeader.e_ident(llvm::ELF::EI_VERSION, llvm::ELF::EV_CURRENT); + elfHeader.e_ident(llvm::ELF::EI_OSABI, llvm::ELF::ELFOSABI_NONE); + if (_targetLayout.findOutputSection(".got.plt")) + elfHeader.e_ident(llvm::ELF::EI_ABIVERSION, 1); + else + elfHeader.e_ident(llvm::ELF::EI_ABIVERSION, 0); + + elfHeader.e_flags(_ctx.getMergedELFFlags()); + } + + void finalizeMipsRuntimeAtomValues() { + if (!_ctx.isDynamic()) + return; + + auto gotSection = _targetLayout.findOutputSection(".got"); + auto got = gotSection ? gotSection->virtualAddr() : 0; + auto gp = gotSection ? got + _targetLayout.getGPOffset() : 0; + + setAtomValue("_GLOBAL_OFFSET_TABLE_", got); + setAtomValue("_gp", gp); + setAtomValue("_gp_disp", gp); + setAtomValue("__gnu_local_gp", gp); + } + + bool hasGlobalGOTEntry(const Atom *a) const { + return _targetLayout.getGOTSection().hasGlobalGOTEntry(a); + } + + std::unique_ptr<MipsRuntimeFile<ELFT>> createRuntimeFile() { + auto file = llvm::make_unique<MipsRuntimeFile<ELFT>>(_ctx); + if (_ctx.isDynamic()) { + file->addAbsoluteAtom("_GLOBAL_OFFSET_TABLE_"); + file->addAbsoluteAtom("_gp"); + file->addAbsoluteAtom("_gp_disp"); + file->addAbsoluteAtom("__gnu_local_gp"); + } + return file; + } + +private: + MipsLinkingContext &_ctx; + MipsTargetLayout<ELFT> &_targetLayout; + + void setAtomValue(StringRef name, uint64_t value) { + auto atom = _targetLayout.findAbsoluteAtom(name); + assert(atom != _targetLayout.absoluteAtoms().end()); + (*atom)->_virtualAddr = value; + } +}; + +} // elf +} // lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsExecutableWriter.h b/lib/ReaderWriter/ELF/Mips/MipsExecutableWriter.h new file mode 100644 index 0000000000000..1a85bba3bd0f6 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsExecutableWriter.h @@ -0,0 +1,154 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsExecutableWriter.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_MIPS_MIPS_EXECUTABLE_WRITER_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_EXECUTABLE_WRITER_H + +#include "ExecutableWriter.h" +#include "MipsDynamicTable.h" +#include "MipsELFWriters.h" +#include "MipsLinkingContext.h" + +namespace lld { +namespace elf { + +template <typename ELFT> class MipsTargetLayout; + +template <class ELFT> +class MipsExecutableWriter : public ExecutableWriter<ELFT> { +public: + MipsExecutableWriter(MipsLinkingContext &ctx, MipsTargetLayout<ELFT> &layout); + +protected: + void buildDynamicSymbolTable(const File &file) override; + + // Add any runtime files and their atoms to the output + bool createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; + + void finalizeDefaultAtomValues() override; + std::error_code setELFHeader() override; + + unique_bump_ptr<SymbolTable<ELFT>> createSymbolTable() override; + unique_bump_ptr<DynamicTable<ELFT>> createDynamicTable() override; + + unique_bump_ptr<DynamicSymbolTable<ELFT>> + createDynamicSymbolTable() override; + +private: + MipsELFWriter<ELFT> _writeHelper; + MipsTargetLayout<ELFT> &_mipsTargetLayout; +}; + +template <class ELFT> +MipsExecutableWriter<ELFT>::MipsExecutableWriter(MipsLinkingContext &ctx, + MipsTargetLayout<ELFT> &layout) + : ExecutableWriter<ELFT>(ctx, layout), _writeHelper(ctx, layout), + _mipsTargetLayout(layout) {} + +template <class ELFT> +std::error_code MipsExecutableWriter<ELFT>::setELFHeader() { + std::error_code ec = ExecutableWriter<ELFT>::setELFHeader(); + if (ec) + return ec; + + StringRef entryName = this->_context.entrySymbolName(); + if (const AtomLayout *al = this->_layout.findAtomLayoutByName(entryName)) { + const auto *ea = cast<DefinedAtom>(al->_atom); + if (ea->codeModel() == DefinedAtom::codeMipsMicro || + ea->codeModel() == DefinedAtom::codeMipsMicroPIC) + // Adjust entry symbol value if this symbol is microMIPS encoded. + this->_elfHeader->e_entry(al->_virtualAddr | 1); + } + + _writeHelper.setELFHeader(*this->_elfHeader); + return std::error_code(); +} + +template <class ELFT> +void MipsExecutableWriter<ELFT>::buildDynamicSymbolTable(const File &file) { + // MIPS ABI requires to add to dynsym even undefined symbols + // if they have a corresponding entries in a global part of GOT. + for (auto sec : this->_layout.sections()) + if (auto section = dyn_cast<AtomSection<ELFT>>(sec)) + for (const auto &atom : section->atoms()) { + if (_writeHelper.hasGlobalGOTEntry(atom->_atom)) { + this->_dynamicSymbolTable->addSymbol(atom->_atom, section->ordinal(), + atom->_virtualAddr, atom); + continue; + } + + const DefinedAtom *da = dyn_cast<const DefinedAtom>(atom->_atom); + if (!da) + continue; + + if (da->dynamicExport() != DefinedAtom::dynamicExportAlways && + !this->_context.isDynamicallyExportedSymbol(da->name()) && + !(this->_context.shouldExportDynamic() && + da->scope() == Atom::Scope::scopeGlobal)) + continue; + + this->_dynamicSymbolTable->addSymbol(atom->_atom, section->ordinal(), + atom->_virtualAddr, atom); + } + + for (const UndefinedAtom *a : file.undefined()) + // FIXME (simon): Consider to move this check to the + // MipsELFUndefinedAtom class method. That allows to + // handle more complex coditions in the future. + if (_writeHelper.hasGlobalGOTEntry(a)) + this->_dynamicSymbolTable->addSymbol(a, ELF::SHN_UNDEF); + + // Skip our immediate parent class method + // ExecutableWriter<ELFT>::buildDynamicSymbolTable because we replaced it + // with our own version. Call OutputELFWriter directly. + OutputELFWriter<ELFT>::buildDynamicSymbolTable(file); +} + +template <class ELFT> +bool MipsExecutableWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + ExecutableWriter<ELFT>::createImplicitFiles(result); + result.push_back(std::move(_writeHelper.createRuntimeFile())); + return true; +} + +template <class ELFT> +void MipsExecutableWriter<ELFT>::finalizeDefaultAtomValues() { + // Finalize the atom values that are part of the parent. + ExecutableWriter<ELFT>::finalizeDefaultAtomValues(); + _writeHelper.finalizeMipsRuntimeAtomValues(); +} + +template <class ELFT> +unique_bump_ptr<SymbolTable<ELFT>> + MipsExecutableWriter<ELFT>::createSymbolTable() { + return unique_bump_ptr<SymbolTable<ELFT>>(new ( + this->_alloc) MipsSymbolTable<ELFT>(this->_context)); +} + +/// \brief create dynamic table +template <class ELFT> +unique_bump_ptr<DynamicTable<ELFT>> + MipsExecutableWriter<ELFT>::createDynamicTable() { + return unique_bump_ptr<DynamicTable<ELFT>>(new ( + this->_alloc) MipsDynamicTable<ELFT>(this->_context, _mipsTargetLayout)); +} + +/// \brief create dynamic symbol table +template <class ELFT> +unique_bump_ptr<DynamicSymbolTable<ELFT>> + MipsExecutableWriter<ELFT>::createDynamicSymbolTable() { + return unique_bump_ptr<DynamicSymbolTable<ELFT>>( + new (this->_alloc) MipsDynamicSymbolTable<ELFT>( + this->_context, _mipsTargetLayout)); +} + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.cpp b/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.cpp new file mode 100644 index 0000000000000..7bffcbeb5c085 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.cpp @@ -0,0 +1,115 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsLinkingContext.cpp -------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Atoms.h" +#include "MipsCtorsOrderPass.h" +#include "MipsLinkingContext.h" +#include "MipsRelocationPass.h" +#include "MipsTargetHandler.h" + +using namespace lld; +using namespace lld::elf; + +std::unique_ptr<ELFLinkingContext> +MipsLinkingContext::create(llvm::Triple triple) { + if (triple.getArch() == llvm::Triple::mipsel || + triple.getArch() == llvm::Triple::mips64el) + return std::unique_ptr<ELFLinkingContext>(new MipsLinkingContext(triple)); + return nullptr; +} + +typedef std::unique_ptr<TargetHandlerBase> TargetHandlerBasePtr; + +static TargetHandlerBasePtr createTarget(llvm::Triple triple, + MipsLinkingContext &ctx) { + switch (triple.getArch()) { + case llvm::Triple::mipsel: + return TargetHandlerBasePtr(new MipsTargetHandler<Mips32ELType>(ctx)); + case llvm::Triple::mips64el: + return TargetHandlerBasePtr(new MipsTargetHandler<Mips64ELType>(ctx)); + default: + llvm_unreachable("Unhandled arch"); + } +} + +MipsLinkingContext::MipsLinkingContext(llvm::Triple triple) + : ELFLinkingContext(triple, createTarget(triple, *this)), + _flagsMerger(triple.isArch64Bit()) {} + +uint32_t MipsLinkingContext::getMergedELFFlags() const { + return _flagsMerger.getMergedELFFlags(); +} + +MipsELFFlagsMerger &MipsLinkingContext::getELFFlagsMerger() { + return _flagsMerger; +} + +uint64_t MipsLinkingContext::getBaseAddress() const { + if (_baseAddress == 0 && getOutputELFType() == llvm::ELF::ET_EXEC) + return getTriple().isArch64Bit() ? 0x120000000 : 0x400000; + return _baseAddress; +} + +StringRef MipsLinkingContext::entrySymbolName() const { + if (_outputELFType == elf::ET_EXEC && _entrySymbolName.empty()) + return "__start"; + return _entrySymbolName; +} + +StringRef MipsLinkingContext::getDefaultInterpreter() const { + return getTriple().isArch64Bit() ? "/lib64/ld.so.1" : "/lib/ld.so.1"; +} + +void MipsLinkingContext::addPasses(PassManager &pm) { + auto pass = createMipsRelocationPass(*this); + if (pass) + pm.add(std::move(pass)); + ELFLinkingContext::addPasses(pm); + pm.add(llvm::make_unique<elf::MipsCtorsOrderPass>()); +} + +bool MipsLinkingContext::isDynamicRelocation(const Reference &r) const { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::Mips); + switch (r.kindValue()) { + case llvm::ELF::R_MIPS_COPY: + case llvm::ELF::R_MIPS_REL32: + case llvm::ELF::R_MIPS_TLS_DTPMOD32: + case llvm::ELF::R_MIPS_TLS_DTPREL32: + case llvm::ELF::R_MIPS_TLS_TPREL32: + case llvm::ELF::R_MIPS_TLS_DTPMOD64: + case llvm::ELF::R_MIPS_TLS_DTPREL64: + case llvm::ELF::R_MIPS_TLS_TPREL64: + return true; + default: + return false; + } +} + +bool MipsLinkingContext::isCopyRelocation(const Reference &r) const { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::Mips); + if (r.kindValue() == llvm::ELF::R_MIPS_COPY) + return true; + return false; +} + +bool MipsLinkingContext::isPLTRelocation(const Reference &r) const { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::Mips); + switch (r.kindValue()) { + case llvm::ELF::R_MIPS_JUMP_SLOT: + return true; + default: + return false; + } +} diff --git a/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.h b/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.h new file mode 100644 index 0000000000000..824605f5fa7f2 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsLinkingContext.h @@ -0,0 +1,68 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsLinkingContext.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_MIPS_MIPS_LINKING_CONTEXT_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_LINKING_CONTEXT_H + +#include "MipsELFFlagsMerger.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" + +namespace lld { +namespace elf { + +/// \brief Mips internal references. +enum { + /// \brief Do nothing but mark GOT entry as a global one. + LLD_R_MIPS_GLOBAL_GOT = 1024, + /// \brief Apply high 16 bits of symbol + addend. + LLD_R_MIPS_32_HI16 = 1025, + /// \brief The same as R_MIPS_26 but for global symbols. + LLD_R_MIPS_GLOBAL_26 = 1026, + /// \brief Setup hi 16 bits using the symbol this reference refers to. + LLD_R_MIPS_HI16 = 1027, + /// \brief Setup low 16 bits using the symbol this reference refers to. + LLD_R_MIPS_LO16 = 1028, + /// \brief Represents a reference between PLT and dynamic symbol. + LLD_R_MIPS_STO_PLT = 1029, + /// \brief The same as R_MICROMIPS_26_S1 but for global symbols. + LLD_R_MICROMIPS_GLOBAL_26_S1 = 1030, + /// \brief Apply high 32+16 bits of symbol + addend. + LLD_R_MIPS_64_HI16 = 1031, +}; + +typedef llvm::object::ELFType<llvm::support::little, 2, false> Mips32ELType; +typedef llvm::object::ELFType<llvm::support::little, 2, true> Mips64ELType; +typedef llvm::object::ELFType<llvm::support::big, 2, false> Mips32BEType; +typedef llvm::object::ELFType<llvm::support::big, 2, true> Mips64BEType; + +class MipsLinkingContext final : public ELFLinkingContext { +public: + static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + MipsLinkingContext(llvm::Triple triple); + + uint32_t getMergedELFFlags() const; + MipsELFFlagsMerger &getELFFlagsMerger(); + + // ELFLinkingContext + uint64_t getBaseAddress() const override; + StringRef entrySymbolName() const override; + StringRef getDefaultInterpreter() const override; + void addPasses(PassManager &pm) override; + bool isRelaOutputFormat() const override { return false; } + bool isDynamicRelocation(const Reference &r) const override; + bool isCopyRelocation(const Reference &r) const override; + bool isPLTRelocation(const Reference &r) const override; + +private: + MipsELFFlagsMerger _flagsMerger; +}; + +} // elf +} // lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp b/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp new file mode 100644 index 0000000000000..173ce0e6b1a87 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp @@ -0,0 +1,606 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.cpp ----------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MipsTargetHandler.h" +#include "MipsLinkingContext.h" +#include "MipsRelocationHandler.h" + +using namespace lld; +using namespace elf; +using namespace llvm::ELF; +using namespace llvm::support; + +namespace { +enum class CrossJumpMode { + None, // Not a jump or non-isa-cross jump + ToRegular, // cross isa jump to regular symbol + ToMicro // cross isa jump to microMips symbol +}; + +struct MipsRelocationParams { + uint8_t _size; // Relocations's size in bytes + uint64_t _mask; // Read/write mask of relocation + uint8_t _shift; // Relocation's addendum left shift size + bool _shuffle; // Relocation's addendum/result needs to be shuffled +}; + +template <class ELFT> class RelocationHandler : public MipsRelocationHandler { +public: + RelocationHandler(MipsLinkingContext &ctx) : _ctx(ctx) {} + + std::error_code applyRelocation(ELFWriter &writer, + llvm::FileOutputBuffer &buf, + const lld::AtomLayout &atom, + const Reference &ref) const override; + + Reference::Addend readAddend(Reference::KindValue kind, + const uint8_t *content) const override; + +private: + MipsLinkingContext &_ctx; +}; +} + +static MipsRelocationParams getRelocationParams(uint32_t rType) { + switch (rType) { + case R_MIPS_NONE: + return {4, 0x0, 0, false}; + case R_MIPS_64: + case R_MIPS_SUB: + return {8, 0xffffffffffffffffull, 0, false}; + case R_MIPS_32: + case R_MIPS_GPREL32: + case R_MIPS_PC32: + return {4, 0xffffffff, 0, false}; + case LLD_R_MIPS_32_HI16: + return {4, 0xffff0000, 0, false}; + case LLD_R_MIPS_64_HI16: + return {8, 0xffffffffffff0000ull, 0, false}; + case R_MIPS_26: + case LLD_R_MIPS_GLOBAL_26: + return {4, 0x3ffffff, 2, false}; + case R_MIPS_PC18_S3: + return {4, 0x3ffff, 3, false}; + case R_MIPS_PC19_S2: + return {4, 0x7ffff, 2, false}; + case R_MIPS_PC21_S2: + return {4, 0x1fffff, 2, false}; + case R_MIPS_PC26_S2: + return {4, 0x3ffffff, 2, false}; + case R_MIPS_HI16: + case R_MIPS_LO16: + case R_MIPS_PCHI16: + case R_MIPS_PCLO16: + case R_MIPS_GPREL16: + case R_MIPS_GOT16: + case R_MIPS_GOT_DISP: + case R_MIPS_GOT_PAGE: + case R_MIPS_GOT_OFST: + case R_MIPS_TLS_DTPREL_HI16: + case R_MIPS_TLS_DTPREL_LO16: + case R_MIPS_TLS_TPREL_HI16: + case R_MIPS_TLS_TPREL_LO16: + case LLD_R_MIPS_HI16: + case LLD_R_MIPS_LO16: + return {4, 0xffff, 0, false}; + case R_MICROMIPS_TLS_DTPREL_HI16: + case R_MICROMIPS_TLS_DTPREL_LO16: + case R_MICROMIPS_TLS_TPREL_HI16: + case R_MICROMIPS_TLS_TPREL_LO16: + return {4, 0xffff, 0, true}; + case R_MICROMIPS_26_S1: + case LLD_R_MICROMIPS_GLOBAL_26_S1: + return {4, 0x3ffffff, 1, true}; + case R_MICROMIPS_HI16: + case R_MICROMIPS_LO16: + case R_MICROMIPS_GOT16: + return {4, 0xffff, 0, true}; + case R_MICROMIPS_PC16_S1: + return {4, 0xffff, 1, true}; + case R_MICROMIPS_PC7_S1: + return {4, 0x7f, 1, false}; + case R_MICROMIPS_PC10_S1: + return {4, 0x3ff, 1, false}; + case R_MICROMIPS_PC23_S2: + return {4, 0x7fffff, 2, true}; + case R_MIPS_CALL16: + case R_MIPS_TLS_GD: + case R_MIPS_TLS_LDM: + case R_MIPS_TLS_GOTTPREL: + return {4, 0xffff, 0, false}; + case R_MICROMIPS_CALL16: + case R_MICROMIPS_TLS_GD: + case R_MICROMIPS_TLS_LDM: + case R_MICROMIPS_TLS_GOTTPREL: + return {4, 0xffff, 0, true}; + case R_MIPS_JALR: + return {4, 0x0, 0, false}; + case R_MICROMIPS_JALR: + return {4, 0x0, 0, true}; + case R_MIPS_REL32: + case R_MIPS_JUMP_SLOT: + case R_MIPS_COPY: + case R_MIPS_TLS_DTPMOD32: + case R_MIPS_TLS_DTPREL32: + case R_MIPS_TLS_TPREL32: + // Ignore runtime relocations. + return {4, 0x0, 0, false}; + case R_MIPS_TLS_DTPMOD64: + case R_MIPS_TLS_DTPREL64: + case R_MIPS_TLS_TPREL64: + return {8, 0x0, 0, false}; + case LLD_R_MIPS_GLOBAL_GOT: + case LLD_R_MIPS_STO_PLT: + // Do nothing. + return {4, 0x0, 0, false}; + default: + llvm_unreachable("Unknown relocation"); + } +} + +/// \brief R_MIPS_32 +/// local/external: word32 S + A (truncate) +static uint32_t reloc32(uint64_t S, int64_t A) { return S + A; } + +/// \brief R_MIPS_64 +/// local/external: word64 S + A (truncate) +static uint64_t reloc64(uint64_t S, int64_t A) { return S + A; } + +/// \brief R_MIPS_SUB +/// local/external: word64 S - A (truncate) +static uint64_t relocSub(uint64_t S, int64_t A) { return S - A; } + +/// \brief R_MIPS_PC32 +/// local/external: word32 S + A i- P (truncate) +static uint32_t relocpc32(uint64_t P, uint64_t S, int64_t A) { + return S + A - P; +} + +/// \brief R_MIPS_26, R_MICROMIPS_26_S1 +/// local : ((A | ((P + 4) & 0x3F000000)) + S) >> 2 +static uint32_t reloc26loc(uint64_t P, uint64_t S, int32_t A, uint32_t shift) { + uint32_t result = (A | ((P + 4) & (0xfc000000 << shift))) + S; + return result >> shift; +} + +/// \brief LLD_R_MIPS_GLOBAL_26, LLD_R_MICROMIPS_GLOBAL_26_S1 +/// external: (sign-extend(A) + S) >> 2 +static uint32_t reloc26ext(uint64_t S, int32_t A, uint32_t shift) { + int32_t result = + shift == 1 ? llvm::SignExtend32<27>(A) : llvm::SignExtend32<28>(A); + return (result + S) >> shift; +} + +/// \brief R_MIPS_HI16, R_MIPS_TLS_DTPREL_HI16, R_MIPS_TLS_TPREL_HI16, +/// R_MICROMIPS_HI16, R_MICROMIPS_TLS_DTPREL_HI16, R_MICROMIPS_TLS_TPREL_HI16, +/// LLD_R_MIPS_HI16 +/// local/external: hi16 (AHL + S) - (short)(AHL + S) (truncate) +/// _gp_disp : hi16 (AHL + GP - P) - (short)(AHL + GP - P) (verify) +static uint32_t relocHi16(uint64_t P, uint64_t S, int64_t AHL, bool isGPDisp) { + int32_t result = isGPDisp ? AHL + S - P : AHL + S; + return (result + 0x8000) >> 16; +} + +/// \brief R_MIPS_PCHI16 +/// local/external: hi16 (S + AHL - P) +static uint32_t relocPcHi16(uint64_t P, uint64_t S, int64_t AHL) { + int32_t result = S + AHL - P; + return (result + 0x8000) >> 16; +} + +/// \brief R_MIPS_LO16, R_MIPS_TLS_DTPREL_LO16, R_MIPS_TLS_TPREL_LO16, +/// R_MICROMIPS_LO16, R_MICROMIPS_TLS_DTPREL_LO16, R_MICROMIPS_TLS_TPREL_LO16, +/// LLD_R_MIPS_LO16 +/// local/external: lo16 AHL + S (truncate) +/// _gp_disp : lo16 AHL + GP - P + 4 (verify) +static uint32_t relocLo16(uint64_t P, uint64_t S, int64_t AHL, bool isGPDisp, + bool micro) { + int32_t result = isGPDisp ? AHL + S - P + (micro ? 3 : 4) : AHL + S; + return result; +} + +/// \brief R_MIPS_PCLO16 +/// local/external: lo16 (S + AHL - P) +static uint32_t relocPcLo16(uint64_t P, uint64_t S, int64_t AHL) { + AHL = llvm::SignExtend32<16>(AHL); + int32_t result = S + AHL - P; + return result; +} + +/// \brief R_MIPS_GOT16, R_MIPS_CALL16, R_MICROMIPS_GOT16, R_MICROMIPS_CALL16 +/// rel16 G (verify) +static uint64_t relocGOT(uint64_t S, uint64_t GP) { + int64_t G = (int64_t)(S - GP); + return G; +} + +/// R_MIPS_GOT_OFST +/// rel16 offset of (S+A) from the page pointer (verify) +static uint32_t relocGOTOfst(uint64_t S, int64_t A) { + uint64_t page = (S + A + 0x8000) & ~0xffff; + return S + A - page; +} + +/// \brief R_MIPS_GPREL16 +/// local: sign-extend(A) + S + GP0 - GP +/// external: sign-extend(A) + S - GP +static uint64_t relocGPRel16(uint64_t S, int64_t A, uint64_t GP) { + // We added GP0 to addendum for a local symbol during a Relocation pass. + return llvm::SignExtend32<16>(A) + S - GP; +} + +/// \brief R_MIPS_GPREL32 +/// local: rel32 A + S + GP0 - GP (truncate) +static uint64_t relocGPRel32(uint64_t S, int64_t A, uint64_t GP) { + // We added GP0 to addendum for a local symbol during a Relocation pass. + return A + S - GP; +} + +/// \brief R_MIPS_PC18_S3 +/// local/external: (S + A - P) >> 3 (P with cleared 3 less significant bits) +static uint32_t relocPc18(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<21>(A); + // FIXME (simon): Check that S + A has 8-byte alignment + int32_t result = S + A - ((P | 7) ^ 7); + return result >> 3; +} + +/// \brief R_MIPS_PC19_S2 +/// local/external: (S + A - P) >> 2 +static uint32_t relocPc19(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<21>(A); + // FIXME (simon): Check that S + A has 4-byte alignment + int32_t result = S + A - P; + return result >> 2; +} + +/// \brief R_MIPS_PC21_S2 +/// local/external: (S + A - P) >> 2 +static uint32_t relocPc21(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<23>(A); + // FIXME (simon): Check that S + A has 4-byte alignment + int32_t result = S + A - P; + return result >> 2; +} + +/// \brief R_MIPS_PC26_S2 +/// local/external: (S + A - P) >> 2 +static uint32_t relocPc26(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<28>(A); + // FIXME (simon): Check that S + A has 4-byte alignment + int32_t result = S + A - P; + return result >> 2; +} + +/// \brief R_MICROMIPS_PC7_S1 +static uint32_t relocPc7(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<8>(A); + int32_t result = S + A - P; + return result >> 1; +} + +/// \brief R_MICROMIPS_PC10_S1 +static uint32_t relocPc10(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<11>(A); + int32_t result = S + A - P; + return result >> 1; +} + +/// \brief R_MICROMIPS_PC16_S1 +static uint32_t relocPc16(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<17>(A); + int32_t result = S + A - P; + return result >> 1; +} + +/// \brief R_MICROMIPS_PC23_S2 +static uint32_t relocPc23(uint64_t P, uint64_t S, int64_t A) { + A = llvm::SignExtend32<25>(A); + int32_t result = S + A - P; + + // Check addiupc 16MB range. + if (result + 0x1000000 >= 0x2000000) + llvm::errs() << "The addiupc instruction immediate " + << llvm::format_hex(result, 10) << " is out of range.\n"; + + return result >> 2; +} + +/// \brief LLD_R_MIPS_32_HI16, LLD_R_MIPS_64_HI16 +static uint64_t relocMaskLow16(uint64_t S, int64_t A) { + return S + A + 0x8000; +} + +static std::error_code adjustJumpOpCode(uint64_t &ins, uint64_t tgt, + CrossJumpMode mode) { + if (mode == CrossJumpMode::None) + return std::error_code(); + + bool toMicro = mode == CrossJumpMode::ToMicro; + uint32_t opNative = toMicro ? 0x03 : 0x3d; + uint32_t opCross = toMicro ? 0x1d : 0x3c; + + if ((tgt & 1) != toMicro) + return make_dynamic_error_code( + Twine("Incorrect bit 0 for the jalx target")); + + if (tgt & 2) + return make_dynamic_error_code(Twine("The jalx target 0x") + + Twine::utohexstr(tgt) + + " is not word-aligned"); + uint8_t op = ins >> 26; + if (op != opNative && op != opCross) + return make_dynamic_error_code(Twine("Unsupported jump opcode (0x") + + Twine::utohexstr(op) + + ") for ISA modes cross call"); + + ins = (ins & ~(0x3f << 26)) | (opCross << 26); + return std::error_code(); +} + +static bool isMicroMipsAtom(const Atom *a) { + if (const auto *da = dyn_cast<DefinedAtom>(a)) + return da->codeModel() == DefinedAtom::codeMipsMicro || + da->codeModel() == DefinedAtom::codeMipsMicroPIC; + return false; +} + +static CrossJumpMode getCrossJumpMode(const Reference &ref) { + if (!isa<DefinedAtom>(ref.target())) + return CrossJumpMode::None; + bool isTgtMicro = isMicroMipsAtom(ref.target()); + switch (ref.kindValue()) { + case R_MIPS_26: + case LLD_R_MIPS_GLOBAL_26: + return isTgtMicro ? CrossJumpMode::ToMicro : CrossJumpMode::None; + case R_MICROMIPS_26_S1: + case LLD_R_MICROMIPS_GLOBAL_26_S1: + return isTgtMicro ? CrossJumpMode::None : CrossJumpMode::ToRegular; + default: + return CrossJumpMode::None; + } +} + +static uint32_t microShuffle(uint32_t ins) { + return ((ins & 0xffff) << 16) | ((ins & 0xffff0000) >> 16); +} + +static ErrorOr<uint64_t> calculateRelocation(Reference::KindValue kind, + Reference::Addend addend, + uint64_t tgtAddr, uint64_t relAddr, + uint64_t gpAddr, bool isGP, + CrossJumpMode jumpMode) { + bool isCrossJump = jumpMode != CrossJumpMode::None; + switch (kind) { + case R_MIPS_NONE: + return 0; + case R_MIPS_32: + return reloc32(tgtAddr, addend); + case R_MIPS_64: + return reloc64(tgtAddr, addend); + case R_MIPS_SUB: + return relocSub(tgtAddr, addend); + case R_MIPS_26: + return reloc26loc(relAddr, tgtAddr, addend, 2); + case R_MICROMIPS_26_S1: + return reloc26loc(relAddr, tgtAddr, addend, isCrossJump ? 2 : 1); + case R_MIPS_HI16: + case R_MICROMIPS_HI16: + return relocHi16(relAddr, tgtAddr, addend, isGP); + case R_MIPS_PCHI16: + return relocPcHi16(relAddr, tgtAddr, addend); + case R_MIPS_LO16: + return relocLo16(relAddr, tgtAddr, addend, isGP, false); + case R_MIPS_PCLO16: + return relocPcLo16(relAddr, tgtAddr, addend); + case R_MICROMIPS_LO16: + return relocLo16(relAddr, tgtAddr, addend, isGP, true); + case R_MIPS_GOT16: + case R_MIPS_CALL16: + case R_MIPS_GOT_DISP: + case R_MIPS_GOT_PAGE: + case R_MICROMIPS_GOT16: + case R_MICROMIPS_CALL16: + case R_MIPS_TLS_GD: + case R_MIPS_TLS_LDM: + case R_MIPS_TLS_GOTTPREL: + case R_MICROMIPS_TLS_GD: + case R_MICROMIPS_TLS_LDM: + case R_MICROMIPS_TLS_GOTTPREL: + return relocGOT(tgtAddr, gpAddr); + case R_MIPS_GOT_OFST: + return relocGOTOfst(tgtAddr, addend); + case R_MIPS_PC18_S3: + return relocPc18(relAddr, tgtAddr, addend); + case R_MIPS_PC19_S2: + return relocPc19(relAddr, tgtAddr, addend); + case R_MIPS_PC21_S2: + return relocPc21(relAddr, tgtAddr, addend); + case R_MIPS_PC26_S2: + return relocPc26(relAddr, tgtAddr, addend); + case R_MICROMIPS_PC7_S1: + return relocPc7(relAddr, tgtAddr, addend); + case R_MICROMIPS_PC10_S1: + return relocPc10(relAddr, tgtAddr, addend); + case R_MICROMIPS_PC16_S1: + return relocPc16(relAddr, tgtAddr, addend); + case R_MICROMIPS_PC23_S2: + return relocPc23(relAddr, tgtAddr, addend); + case R_MIPS_TLS_DTPREL_HI16: + case R_MIPS_TLS_TPREL_HI16: + case R_MICROMIPS_TLS_DTPREL_HI16: + case R_MICROMIPS_TLS_TPREL_HI16: + return relocHi16(0, tgtAddr, addend, false); + case R_MIPS_TLS_DTPREL_LO16: + case R_MIPS_TLS_TPREL_LO16: + return relocLo16(0, tgtAddr, addend, false, false); + case R_MICROMIPS_TLS_DTPREL_LO16: + case R_MICROMIPS_TLS_TPREL_LO16: + return relocLo16(0, tgtAddr, addend, false, true); + case R_MIPS_GPREL16: + return relocGPRel16(tgtAddr, addend, gpAddr); + case R_MIPS_GPREL32: + return relocGPRel32(tgtAddr, addend, gpAddr); + case R_MIPS_JALR: + case R_MICROMIPS_JALR: + // We do not do JALR optimization now. + return 0; + case R_MIPS_REL32: + case R_MIPS_JUMP_SLOT: + case R_MIPS_COPY: + case R_MIPS_TLS_DTPMOD32: + case R_MIPS_TLS_DTPREL32: + case R_MIPS_TLS_TPREL32: + case R_MIPS_TLS_DTPMOD64: + case R_MIPS_TLS_DTPREL64: + case R_MIPS_TLS_TPREL64: + // Ignore runtime relocations. + return 0; + case R_MIPS_PC32: + return relocpc32(relAddr, tgtAddr, addend); + case LLD_R_MIPS_GLOBAL_GOT: + // Do nothing. + case LLD_R_MIPS_32_HI16: + case LLD_R_MIPS_64_HI16: + return relocMaskLow16(tgtAddr, addend); + case LLD_R_MIPS_GLOBAL_26: + return reloc26ext(tgtAddr, addend, 2); + case LLD_R_MICROMIPS_GLOBAL_26_S1: + return reloc26ext(tgtAddr, addend, isCrossJump ? 2 : 1); + case LLD_R_MIPS_HI16: + return relocHi16(0, tgtAddr, 0, false); + case LLD_R_MIPS_LO16: + return relocLo16(0, tgtAddr, 0, false, false); + case LLD_R_MIPS_STO_PLT: + // Do nothing. + return 0; + default: + return make_unhandled_reloc_error(); + } +} + +template <class ELFT> +static uint64_t relocRead(const MipsRelocationParams ¶ms, + const uint8_t *loc) { + uint64_t data; + switch (params._size) { + case 4: + data = endian::read<uint32_t, ELFT::TargetEndianness, unaligned>(loc); + break; + case 8: + data = endian::read<uint64_t, ELFT::TargetEndianness, unaligned>(loc); + break; + default: + llvm_unreachable("Unexpected size"); + } + if (params._shuffle) + data = microShuffle(data); + return data; +} + +template <class ELFT> +static void relocWrite(uint64_t data, const MipsRelocationParams ¶ms, + uint8_t *loc) { + if (params._shuffle) + data = microShuffle(data); + switch (params._size) { + case 4: + endian::write<uint32_t, ELFT::TargetEndianness, unaligned>(loc, data); + break; + case 8: + endian::write<uint64_t, ELFT::TargetEndianness, unaligned>(loc, data); + break; + default: + llvm_unreachable("Unexpected size"); + } +} + +template <class ELFT> +std::error_code RelocationHandler<ELFT>::applyRelocation( + ELFWriter &writer, llvm::FileOutputBuffer &buf, const lld::AtomLayout &atom, + const Reference &ref) const { + if (ref.kindNamespace() != lld::Reference::KindNamespace::ELF) + return std::error_code(); + assert(ref.kindArch() == Reference::KindArch::Mips); + + auto &targetLayout = static_cast<MipsTargetLayout<ELFT> &>( + _ctx.getTargetHandler<ELFT>().getTargetLayout()); + + AtomLayout *gpAtom = targetLayout.getGP(); + uint64_t gpAddr = gpAtom ? gpAtom->_virtualAddr : 0; + + AtomLayout *gpDispAtom = targetLayout.getGPDisp(); + bool isGpDisp = gpDispAtom && ref.target() == gpDispAtom->_atom; + + uint8_t *atomContent = buf.getBufferStart() + atom._fileOffset; + uint8_t *location = atomContent + ref.offsetInAtom(); + uint64_t tgtAddr = writer.addressOfAtom(ref.target()); + uint64_t relAddr = atom._virtualAddr + ref.offsetInAtom(); + + if (isMicroMipsAtom(ref.target())) + tgtAddr |= 1; + + CrossJumpMode jumpMode = getCrossJumpMode(ref); + + ErrorOr<uint64_t> res = + calculateRelocation(ref.kindValue(), ref.addend(), tgtAddr, relAddr, + gpAddr, isGpDisp, jumpMode); + if (auto ec = res.getError()) + return ec; + + Reference::KindValue op = ref.kindValue(); + + // FIXME (simon): Handle r_ssym value. + for (auto tag = (ref.tag() & 0xffff); tag & 0xff; tag >>= 8) { + op = tag & 0xff; + res = calculateRelocation(op, *res, 0, relAddr, gpAddr, isGpDisp, jumpMode); + if (auto ec = res.getError()) + return ec; + } + + auto params = getRelocationParams(op); + uint64_t ins = relocRead<ELFT>(params, location); + + if (auto ec = adjustJumpOpCode(ins, tgtAddr, jumpMode)) + return ec; + + ins = (ins & ~params._mask) | (*res & params._mask); + relocWrite<ELFT>(ins, params, location); + + return std::error_code(); +} + +template <class ELFT> +Reference::Addend +RelocationHandler<ELFT>::readAddend(Reference::KindValue kind, + const uint8_t *content) const { + auto params = getRelocationParams(kind); + uint64_t ins = relocRead<ELFT>(params, content); + return (ins & params._mask) << params._shift; +} + +namespace lld { +namespace elf { + +template <> +std::unique_ptr<TargetRelocationHandler> +createMipsRelocationHandler<Mips32ELType>(MipsLinkingContext &ctx) { + return std::unique_ptr<TargetRelocationHandler>( + new RelocationHandler<Mips32ELType>(ctx)); +} + +template <> +std::unique_ptr<TargetRelocationHandler> +createMipsRelocationHandler<Mips64ELType>(MipsLinkingContext &ctx) { + return std::unique_ptr<TargetRelocationHandler>( + new RelocationHandler<Mips64ELType>(ctx)); +} + +} // elf +} // lld diff --git a/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.h b/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.h new file mode 100644 index 0000000000000..87066b2b5c101 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsRelocationHandler.h @@ -0,0 +1,31 @@ +//===- lld/ReaderWriter/ELF/Mips/MipsRelocationHandler.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_MIPS_MIPS_RELOCATION_HANDLER_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_RELOCATION_HANDLER_H + +#include "TargetHandler.h" +#include "lld/Core/Reference.h" + +namespace lld { +namespace elf { + +class MipsRelocationHandler : public TargetRelocationHandler { +public: + virtual Reference::Addend readAddend(Reference::KindValue kind, + const uint8_t *content) const = 0; +}; + +template <class ELFT> +std::unique_ptr<TargetRelocationHandler> +createMipsRelocationHandler(MipsLinkingContext &ctx); + +} // elf +} // lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsRelocationPass.cpp b/lib/ReaderWriter/ELF/Mips/MipsRelocationPass.cpp new file mode 100644 index 0000000000000..a1b3530dfcdf6 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsRelocationPass.cpp @@ -0,0 +1,1070 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsRelocationPass.cpp -------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MipsELFFile.h" +#include "MipsLinkingContext.h" +#include "MipsRelocationPass.h" +#include "MipsTargetHandler.h" +#include "llvm/ADT/DenseSet.h" + +using namespace lld; +using namespace lld::elf; +using namespace llvm::ELF; + +// Lazy resolver +static const uint8_t mipsGot0AtomContent[] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +// Module pointer +static const uint8_t mipsGotModulePointerAtomContent[] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80 +}; + +// TLS GD Entry +static const uint8_t mipsGotTlsGdAtomContent[] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +// Regular PLT0 entry +static const uint8_t mipsPlt0AtomContent[] = { + 0x00, 0x00, 0x1c, 0x3c, // lui $28, %hi(&GOTPLT[0]) + 0x00, 0x00, 0x99, 0x8f, // lw $25, %lo(&GOTPLT[0])($28) + 0x00, 0x00, 0x9c, 0x27, // addiu $28, $28, %lo(&GOTPLT[0]) + 0x23, 0xc0, 0x1c, 0x03, // subu $24, $24, $28 + 0x21, 0x78, 0xe0, 0x03, // move $15, $31 + 0x82, 0xc0, 0x18, 0x00, // srl $24, $24, 2 + 0x09, 0xf8, 0x20, 0x03, // jalr $25 + 0xfe, 0xff, 0x18, 0x27 // subu $24, $24, 2 +}; + +// microMIPS PLT0 entry +static const uint8_t micromipsPlt0AtomContent[] = { + 0x80, 0x79, 0x00, 0x00, // addiupc $3, (&GOTPLT[0]) - . + 0x23, 0xff, 0x00, 0x00, // lw $25, 0($3) + 0x35, 0x05, // subu $2, $2, $3 + 0x25, 0x25, // srl $2, $2, 2 + 0x02, 0x33, 0xfe, 0xff, // subu $24, $2, 2 + 0xff, 0x0d, // move $15, $31 + 0xf9, 0x45, // jalrs $25 + 0x83, 0x0f, // move $28, $3 + 0x00, 0x0c // nop +}; + +// Regular PLT entry +static const uint8_t mipsPltAAtomContent[] = { + 0x00, 0x00, 0x0f, 0x3c, // lui $15, %hi(.got.plt entry) + 0x00, 0x00, 0xf9, 0x8d, // l[wd] $25, %lo(.got.plt entry)($15) + 0x08, 0x00, 0x20, 0x03, // jr $25 + 0x00, 0x00, 0xf8, 0x25 // addiu $24, $15, %lo(.got.plt entry) +}; + +// microMIPS PLT entry +static const uint8_t micromipsPltAtomContent[] = { + 0x00, 0x79, 0x00, 0x00, // addiupc $2, (.got.plt entry) - . + 0x22, 0xff, 0x00, 0x00, // lw $25, 0($2) + 0x99, 0x45, // jr $25 + 0x02, 0x0f // move $24, $2 +}; + +// R6 PLT entry +static const uint8_t mipsR6PltAAtomContent[] = { + 0x00, 0x00, 0x0f, 0x3c, // lui $15, %hi(.got.plt entry) + 0x00, 0x00, 0xf9, 0x8d, // l[wd] $25, %lo(.got.plt entry)($15) + 0x09, 0x00, 0x20, 0x03, // jr $25 + 0x00, 0x00, 0xf8, 0x25 // addiu $24, $15, %lo(.got.plt entry) +}; + +// LA25 stub entry +static const uint8_t mipsLA25AtomContent[] = { + 0x00, 0x00, 0x19, 0x3c, // lui $25, %hi(func) + 0x00, 0x00, 0x00, 0x08, // j func + 0x00, 0x00, 0x39, 0x27, // addiu $25, $25, %lo(func) + 0x00, 0x00, 0x00, 0x00 // nop +}; + +// microMIPS LA25 stub entry +static const uint8_t micromipsLA25AtomContent[] = { + 0xb9, 0x41, 0x00, 0x00, // lui $25, %hi(func) + 0x00, 0xd4, 0x00, 0x00, // j func + 0x39, 0x33, 0x00, 0x00, // addiu $25, $25, %lo(func) + 0x00, 0x00, 0x00, 0x00 // nop +}; + +namespace { + +/// \brief Abstract base class represent MIPS GOT entries. +class MipsGOTAtom : public GOTAtom { +public: + MipsGOTAtom(const File &f) : GOTAtom(f, ".got") {} + + Alignment alignment() const override { return Alignment(2); } +}; + +/// \brief MIPS GOT entry initialized by zero. +template <typename ELFT> class GOT0Atom : public MipsGOTAtom { +public: + GOT0Atom(const File &f) : MipsGOTAtom(f) {} + + ArrayRef<uint8_t> rawContent() const override; +}; + +template <> ArrayRef<uint8_t> GOT0Atom<Mips32ELType>::rawContent() const { + return llvm::makeArrayRef(mipsGot0AtomContent).slice(4); +} +template <> ArrayRef<uint8_t> GOT0Atom<Mips64ELType>::rawContent() const { + return llvm::makeArrayRef(mipsGot0AtomContent); +} + +/// \brief MIPS GOT entry initialized by zero. +template <typename ELFT> class GOTModulePointerAtom : public MipsGOTAtom { +public: + GOTModulePointerAtom(const File &f) : MipsGOTAtom(f) {} + + ArrayRef<uint8_t> rawContent() const override; +}; + +template <> +ArrayRef<uint8_t> GOTModulePointerAtom<Mips32ELType>::rawContent() const { + return llvm::makeArrayRef(mipsGotModulePointerAtomContent).slice(4); +} +template <> +ArrayRef<uint8_t> GOTModulePointerAtom<Mips64ELType>::rawContent() const { + return llvm::makeArrayRef(mipsGotModulePointerAtomContent); +} + +/// \brief MIPS GOT TLS GD entry. +template <typename ELFT> class GOTTLSGdAtom : public MipsGOTAtom { +public: + GOTTLSGdAtom(const File &f) : MipsGOTAtom(f) {} + + ArrayRef<uint8_t> rawContent() const override; +}; + +template <> ArrayRef<uint8_t> GOTTLSGdAtom<Mips32ELType>::rawContent() const { + return llvm::makeArrayRef(mipsGotTlsGdAtomContent).slice(8); +} + +template <> ArrayRef<uint8_t> GOTTLSGdAtom<Mips64ELType>::rawContent() const { + return llvm::makeArrayRef(mipsGotTlsGdAtomContent); +} + +class GOTPLTAtom : public GOTAtom { +public: + GOTPLTAtom(const File &f) : GOTAtom(f, ".got.plt") {} + GOTPLTAtom(const Atom *a, const File &f) : GOTAtom(f, ".got.plt") { + // Create dynamic relocation to adjust the .got.plt entry at runtime. + addReferenceELF_Mips(R_MIPS_JUMP_SLOT, 0, a, 0); + } + + /// Setup reference to assign initial value to the .got.plt entry. + void setPLT0(const PLTAtom *plt0) { + addReferenceELF_Mips(R_MIPS_32, 0, plt0, 0); + } + + Alignment alignment() const override { return Alignment(2); } + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(mipsGot0AtomContent).slice(4); + } +}; + +class PLT0Atom : public PLTAtom { +public: + PLT0Atom(const Atom *got, const File &f) : PLTAtom(f, ".plt") { + // Setup reference to fixup the PLT0 entry. + addReferenceELF_Mips(LLD_R_MIPS_HI16, 0, got, 0); + addReferenceELF_Mips(LLD_R_MIPS_LO16, 4, got, 0); + addReferenceELF_Mips(LLD_R_MIPS_LO16, 8, got, 0); + } + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(mipsPlt0AtomContent); + } +}; + +class PLT0MicroAtom : public PLTAtom { +public: + PLT0MicroAtom(const Atom *got, const File &f) : PLTAtom(f, ".plt") { + // Setup reference to fixup the PLT0 entry. + addReferenceELF_Mips(R_MICROMIPS_PC23_S2, 0, got, 0); + } + + CodeModel codeModel() const override { return codeMipsMicro; } + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(micromipsPlt0AtomContent); + } +}; + +class PLTAAtom : public PLTAtom { +public: + PLTAAtom(const GOTPLTAtom *got, const File &f) : PLTAtom(f, ".plt") { + // Setup reference to fixup the PLT entry. + addReferenceELF_Mips(LLD_R_MIPS_HI16, 0, got, 0); + addReferenceELF_Mips(LLD_R_MIPS_LO16, 4, got, 0); + addReferenceELF_Mips(LLD_R_MIPS_LO16, 12, got, 0); + } + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(mipsPltAAtomContent); + } +}; + +class PLTR6Atom : public PLTAAtom { +public: + PLTR6Atom(const GOTPLTAtom *got, const File &f) : PLTAAtom(got, f) {} + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(mipsR6PltAAtomContent); + } +}; + +class PLTMicroAtom : public PLTAtom { +public: + PLTMicroAtom(const GOTPLTAtom *got, const File &f) : PLTAtom(f, ".plt") { + // Setup reference to fixup the microMIPS PLT entry. + addReferenceELF_Mips(R_MICROMIPS_PC23_S2, 0, got, 0); + } + + Alignment alignment() const override { return Alignment(1); } + CodeModel codeModel() const override { return codeMipsMicro; } + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(micromipsPltAtomContent); + } +}; + +class LA25Atom : public PLTAtom { +public: + LA25Atom(const File &f) : PLTAtom(f, ".text") {} +}; + +class LA25RegAtom : public LA25Atom { +public: + LA25RegAtom(const Atom *a, const File &f) : LA25Atom(f) { + // Setup reference to fixup the LA25 stub entry. + addReferenceELF_Mips(R_MIPS_HI16, 0, a, 0); + addReferenceELF_Mips(R_MIPS_26, 4, a, 0); + addReferenceELF_Mips(R_MIPS_LO16, 8, a, 0); + } + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(mipsLA25AtomContent); + } +}; + +class LA25MicroAtom : public LA25Atom { +public: + LA25MicroAtom(const Atom *a, const File &f) : LA25Atom(f) { + // Setup reference to fixup the microMIPS LA25 stub entry. + addReferenceELF_Mips(R_MICROMIPS_HI16, 0, a, 0); + addReferenceELF_Mips(R_MICROMIPS_26_S1, 4, a, 0); + addReferenceELF_Mips(R_MICROMIPS_LO16, 8, a, 0); + } + + CodeModel codeModel() const override { return codeMipsMicro; } + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(micromipsLA25AtomContent); + } +}; + +class RelocationPassFile : public SimpleFile { +public: + RelocationPassFile(const ELFLinkingContext &ctx) + : SimpleFile("RelocationPassFile") { + setOrdinal(ctx.getNextOrdinalAndIncrement()); + } + + llvm::BumpPtrAllocator _alloc; +}; + +template <typename ELFT> class RelocationPass : public Pass { +public: + RelocationPass(MipsLinkingContext &ctx); + + void perform(std::unique_ptr<MutableFile> &mf) override; + +private: + /// \brief Reference to the linking context. + const MipsLinkingContext &_ctx; + + /// \brief Owner of all the Atoms created by this pass. + RelocationPassFile _file; + + /// \brief Map Atoms and addend to local GOT entries. + typedef std::pair<const Atom *, int64_t> LocalGotMapKeyT; + llvm::DenseMap<LocalGotMapKeyT, GOTAtom *> _gotLocalMap; + llvm::DenseMap<LocalGotMapKeyT, GOTAtom *> _gotLocalPageMap; + + /// \brief Map Atoms to global GOT entries. + llvm::DenseMap<const Atom *, GOTAtom *> _gotGlobalMap; + + /// \brief Map Atoms to TLS GOT entries. + llvm::DenseMap<const Atom *, GOTAtom *> _gotTLSMap; + + /// \brief Map Atoms to TLS GD GOT entries. + llvm::DenseMap<const Atom *, GOTAtom *> _gotTLSGdMap; + + /// \brief GOT entry for the R_xxxMIPS_TLS_LDM relocations. + GOTTLSGdAtom<ELFT> *_gotLDMEntry; + + /// \brief the list of local GOT atoms. + std::vector<GOTAtom *> _localGotVector; + + /// \brief the list of global GOT atoms. + std::vector<GOTAtom *> _globalGotVector; + + /// \brief the list of TLS GOT atoms. + std::vector<GOTAtom *> _tlsGotVector; + + /// \brief Map Atoms to their GOTPLT entries. + llvm::DenseMap<const Atom *, GOTPLTAtom *> _gotpltMap; + + /// \brief Map Atoms to their PLT entries. + llvm::DenseMap<const Atom *, PLTAAtom *> _pltRegMap; + llvm::DenseMap<const Atom *, PLTMicroAtom *> _pltMicroMap; + + /// \brief Map Atoms to their Object entries. + llvm::DenseMap<const Atom *, ObjectAtom *> _objectMap; + + /// \brief Map Atoms to their LA25 entries. + llvm::DenseMap<const Atom *, LA25RegAtom *> _la25RegMap; + llvm::DenseMap<const Atom *, LA25MicroAtom *> _la25MicroMap; + + /// \brief Atoms referenced by static relocations. + llvm::DenseSet<const Atom *> _hasStaticRelocations; + + /// \brief Atoms require pointers equality. + llvm::DenseSet<const Atom *> _requiresPtrEquality; + + /// \brief References which are candidates for converting + /// to the R_MIPS_REL32 relocation. + std::vector<Reference *> _rel32Candidates; + + /// \brief the list of PLT atoms. + std::vector<PLTAtom *> _pltRegVector; + std::vector<PLTAtom *> _pltMicroVector; + + /// \brief the list of GOTPLT atoms. + std::vector<GOTPLTAtom *> _gotpltVector; + + /// \brief the list of Object entries. + std::vector<ObjectAtom *> _objectVector; + + /// \brief the list of LA25 entries. + std::vector<LA25Atom *> _la25Vector; + + /// \brief Handle a specific reference. + void handleReference(const MipsELFDefinedAtom<ELFT> &atom, Reference &ref); + + /// \brief Collect information about the reference to use it + /// later in the handleReference() routine. + void collectReferenceInfo(const MipsELFDefinedAtom<ELFT> &atom, + Reference &ref); + + void handlePlain(const MipsELFDefinedAtom<ELFT> &atom, Reference &ref); + void handle26(const MipsELFDefinedAtom<ELFT> &atom, Reference &ref); + void handleGOT(Reference &ref); + + const GOTAtom *getLocalGOTEntry(const Reference &ref); + const GOTAtom *getLocalGOTPageEntry(const Reference &ref); + const GOTAtom *getGlobalGOTEntry(const Atom *a); + const GOTAtom *getTLSGOTEntry(const Atom *a); + const GOTAtom *getTLSGdGOTEntry(const Atom *a); + const GOTAtom *getTLSLdmGOTEntry(const Atom *a); + const GOTPLTAtom *getGOTPLTEntry(const Atom *a); + const PLTAtom *getPLTEntry(const Atom *a); + const PLTAtom *getPLTRegEntry(const Atom *a); + const PLTAtom *getPLTMicroEntry(const Atom *a); + const LA25Atom *getLA25Entry(const Atom *target, bool isMicroMips); + const LA25Atom *getLA25RegEntry(const Atom *a); + const LA25Atom *getLA25MicroEntry(const Atom *a); + const ObjectAtom *getObjectEntry(const SharedLibraryAtom *a); + + PLTAtom *createPLTHeader(bool isMicroMips); + + bool isLocal(const Atom *a) const; + bool isLocalCall(const Atom *a) const; + bool isDynamic(const Atom *atom) const; + bool requireLA25Stub(const Atom *a) const; + bool requirePLTEntry(const Atom *a) const; + bool requireCopy(const Atom *a) const; + bool mightBeDynamic(const MipsELFDefinedAtom<ELFT> &atom, + Reference::KindValue refKind) const; + bool hasPLTEntry(const Atom *atom) const; + + bool isR6Target() const; +}; + +template <typename ELFT> +RelocationPass<ELFT>::RelocationPass(MipsLinkingContext &ctx) + : _ctx(ctx), _file(ctx), _gotLDMEntry(nullptr) { + _localGotVector.push_back(new (_file._alloc) GOT0Atom<ELFT>(_file)); + _localGotVector.push_back(new (_file._alloc) + GOTModulePointerAtom<ELFT>(_file)); +} + +template <typename ELFT> +void RelocationPass<ELFT>::perform(std::unique_ptr<MutableFile> &mf) { + for (const auto &atom : mf->defined()) + for (const auto &ref : *atom) + collectReferenceInfo(*cast<MipsELFDefinedAtom<ELFT>>(atom), + const_cast<Reference &>(*ref)); + + // Process all references. + for (const auto &atom : mf->defined()) + for (const auto &ref : *atom) + handleReference(*cast<MipsELFDefinedAtom<ELFT>>(atom), + const_cast<Reference &>(*ref)); + + // Create R_MIPS_REL32 relocations. + for (auto *ref : _rel32Candidates) { + if (!isDynamic(ref->target()) || hasPLTEntry(ref->target())) + continue; + ref->setKindValue(R_MIPS_REL32); + if (ELFT::Is64Bits) + static_cast<MipsELFReference<ELFT> *>(ref)->setTag(R_MIPS_64); + if (!isLocalCall(ref->target())) + getGlobalGOTEntry(ref->target()); + } + + uint64_t ordinal = 0; + + for (auto &got : _localGotVector) { + got->setOrdinal(ordinal++); + mf->addAtom(*got); + } + + for (auto &got : _globalGotVector) { + got->setOrdinal(ordinal++); + mf->addAtom(*got); + } + + for (auto &got : _tlsGotVector) { + got->setOrdinal(ordinal++); + mf->addAtom(*got); + } + + // Create and emit PLT0 entry. + PLTAtom *plt0Atom = nullptr; + if (!_pltRegVector.empty()) + plt0Atom = createPLTHeader(false); + else if (!_pltMicroVector.empty()) + plt0Atom = createPLTHeader(true); + + if (plt0Atom) { + plt0Atom->setOrdinal(ordinal++); + mf->addAtom(*plt0Atom); + } + + // Emit regular PLT entries firts. + for (auto &plt : _pltRegVector) { + plt->setOrdinal(ordinal++); + mf->addAtom(*plt); + } + + // microMIPS PLT entries come after regular ones. + for (auto &plt : _pltMicroVector) { + plt->setOrdinal(ordinal++); + mf->addAtom(*plt); + } + + // Assign PLT0 to GOTPLT entries. + assert(_gotpltMap.empty() || plt0Atom); + for (auto &a: _gotpltMap) + a.second->setPLT0(plt0Atom); + + for (auto &gotplt : _gotpltVector) { + gotplt->setOrdinal(ordinal++); + mf->addAtom(*gotplt); + } + + for (auto obj : _objectVector) { + obj->setOrdinal(ordinal++); + mf->addAtom(*obj); + } + + for (auto la25 : _la25Vector) { + la25->setOrdinal(ordinal++); + mf->addAtom(*la25); + } +} + +template <typename ELFT> +void RelocationPass<ELFT>::handleReference(const MipsELFDefinedAtom<ELFT> &atom, + Reference &ref) { + if (!ref.target()) + return; + if (ref.kindNamespace() != lld::Reference::KindNamespace::ELF) + return; + assert(ref.kindArch() == Reference::KindArch::Mips); + switch (ref.kindValue()) { + case R_MIPS_32: + case R_MIPS_PC32: + case R_MIPS_HI16: + case R_MIPS_LO16: + case R_MIPS_PCHI16: + case R_MIPS_PCLO16: + case R_MICROMIPS_HI16: + case R_MICROMIPS_LO16: + // FIXME (simon): Handle dynamic/static linking differently. + handlePlain(atom, ref); + break; + case R_MIPS_26: + case R_MICROMIPS_26_S1: + handle26(atom, ref); + break; + case R_MIPS_GOT16: + case R_MIPS_CALL16: + case R_MICROMIPS_GOT16: + case R_MICROMIPS_CALL16: + case R_MIPS_GOT_DISP: + case R_MIPS_GOT_PAGE: + handleGOT(ref); + break; + case R_MIPS_GOT_OFST: + // Nothing to do. We create GOT page entry in the R_MIPS_GOT_PAGE handler. + break; + case R_MIPS_GPREL16: + if (isLocal(ref.target())) + ref.setAddend(ref.addend() + atom.file().getGP0()); + break; + case R_MIPS_GPREL32: + ref.setAddend(ref.addend() + atom.file().getGP0()); + break; + case R_MIPS_TLS_DTPREL_HI16: + case R_MIPS_TLS_DTPREL_LO16: + case R_MICROMIPS_TLS_DTPREL_HI16: + case R_MICROMIPS_TLS_DTPREL_LO16: + ref.setAddend(ref.addend() - atom.file().getDTPOffset()); + break; + case R_MIPS_TLS_TPREL_HI16: + case R_MIPS_TLS_TPREL_LO16: + case R_MICROMIPS_TLS_TPREL_HI16: + case R_MICROMIPS_TLS_TPREL_LO16: + ref.setAddend(ref.addend() - atom.file().getTPOffset()); + break; + case R_MIPS_TLS_GD: + case R_MICROMIPS_TLS_GD: + ref.setTarget(getTLSGdGOTEntry(ref.target())); + break; + case R_MIPS_TLS_LDM: + case R_MICROMIPS_TLS_LDM: + ref.setTarget(getTLSLdmGOTEntry(ref.target())); + break; + case R_MIPS_TLS_GOTTPREL: + case R_MICROMIPS_TLS_GOTTPREL: + ref.setTarget(getTLSGOTEntry(ref.target())); + break; + } +} + +template <typename ELFT> +static bool isConstrainSym(const MipsELFDefinedAtom<ELFT> &atom, + Reference::KindValue refKind) { + if ((atom.section()->sh_flags & SHF_ALLOC) == 0) + return false; + switch (refKind) { + case R_MIPS_NONE: + case R_MIPS_JALR: + case R_MICROMIPS_JALR: + case R_MIPS_GPREL16: + case R_MIPS_GPREL32: + return false; + default: + return true; + } +} + +template <typename ELFT> +void RelocationPass<ELFT>::collectReferenceInfo( + const MipsELFDefinedAtom<ELFT> &atom, Reference &ref) { + if (!ref.target()) + return; + if (ref.kindNamespace() != lld::Reference::KindNamespace::ELF) + return; + + auto refKind = ref.kindValue(); + if (!isConstrainSym(atom, refKind)) + return; + + if (mightBeDynamic(atom, refKind)) + _rel32Candidates.push_back(&ref); + else + _hasStaticRelocations.insert(ref.target()); + + if (refKind != R_MIPS_CALL16 && refKind != R_MICROMIPS_CALL16 && + refKind != R_MIPS_26 && refKind != R_MICROMIPS_26_S1) + _requiresPtrEquality.insert(ref.target()); +} + +template <typename ELFT> +bool RelocationPass<ELFT>::isLocal(const Atom *a) const { + if (auto *da = dyn_cast<DefinedAtom>(a)) + return da->scope() == Atom::scopeTranslationUnit; + return false; +} + +template <typename ELFT> +static bool isMipsReadonly(const MipsELFDefinedAtom<ELFT> &atom) { + auto secFlags = atom.section()->sh_flags; + auto secType = atom.section()->sh_type; + + if ((secFlags & SHF_ALLOC) == 0) + return false; + if (secType == SHT_NOBITS) + return false; + if ((secFlags & SHF_WRITE) != 0) + return false; + return true; +} + +template <typename ELFT> +bool RelocationPass<ELFT>::mightBeDynamic(const MipsELFDefinedAtom<ELFT> &atom, + Reference::KindValue refKind) const { + if (refKind == R_MIPS_CALL16 || refKind == R_MIPS_GOT16 || + refKind == R_MICROMIPS_CALL16 || refKind == R_MICROMIPS_GOT16) + return true; + + if (refKind != R_MIPS_32 && refKind != R_MIPS_64) + return false; + if ((atom.section()->sh_flags & SHF_ALLOC) == 0) + return false; + + if (_ctx.getOutputELFType() == ET_DYN) + return true; + if (!isMipsReadonly(atom)) + return true; + if (atom.file().isPIC()) + return true; + + return false; +} + +template <typename ELFT> +bool RelocationPass<ELFT>::hasPLTEntry(const Atom *atom) const { + return _pltRegMap.count(atom) || _pltMicroMap.count(atom); +} + +template <typename ELFT> bool RelocationPass<ELFT>::isR6Target() const { + switch (_ctx.getMergedELFFlags() & EF_MIPS_ARCH) { + case EF_MIPS_ARCH_32R6: + case EF_MIPS_ARCH_64R6: + return true; + default: + return false; + } +} + +template <typename ELFT> +bool RelocationPass<ELFT>::requirePLTEntry(const Atom *a) const { + if (!_hasStaticRelocations.count(a)) + return false; + const auto *sa = dyn_cast<ELFDynamicAtom<ELFT>>(a); + if (sa && sa->type() != SharedLibraryAtom::Type::Code) + return false; + const auto *da = dyn_cast<ELFDefinedAtom<ELFT>>(a); + if (da && da->contentType() != DefinedAtom::typeCode) + return false; + if (isLocalCall(a)) + return false; + return true; +} + +template <typename ELFT> +bool RelocationPass<ELFT>::requireCopy(const Atom *a) const { + if (!_hasStaticRelocations.count(a)) + return false; + const auto *sa = dyn_cast<ELFDynamicAtom<ELFT>>(a); + return sa && sa->type() == SharedLibraryAtom::Type::Data; +} + +template <typename ELFT> +bool RelocationPass<ELFT>::isDynamic(const Atom *atom) const { + const auto *da = dyn_cast<const DefinedAtom>(atom); + if (da && da->dynamicExport() == DefinedAtom::dynamicExportAlways) + return true; + + const auto *sa = dyn_cast<SharedLibraryAtom>(atom); + if (sa) + return true; + + if (_ctx.getOutputELFType() == ET_DYN) { + if (da && da->scope() != DefinedAtom::scopeTranslationUnit) + return true; + + const auto *ua = dyn_cast<UndefinedAtom>(atom); + if (ua) + return true; + } + + return false; +} + +template <typename ELFT> +static bool isMicroMips(const MipsELFDefinedAtom<ELFT> &atom) { + return atom.codeModel() == DefinedAtom::codeMipsMicro || + atom.codeModel() == DefinedAtom::codeMipsMicroPIC; +} + +template <typename ELFT> +const LA25Atom *RelocationPass<ELFT>::getLA25Entry(const Atom *target, + bool isMicroMips) { + return isMicroMips ? getLA25MicroEntry(target) : getLA25RegEntry(target); +} + +template <typename ELFT> +const PLTAtom *RelocationPass<ELFT>::getPLTEntry(const Atom *a) { + bool hasMicroCode = _ctx.getMergedELFFlags() & EF_MIPS_MICROMIPS; + + // If file contains microMIPS code try to reuse compressed PLT entry... + if (hasMicroCode) { + auto microPLT = _pltMicroMap.find(a); + if (microPLT != _pltMicroMap.end()) + return microPLT->second; + } + + // ... then try to reuse a regular PLT entry ... + auto regPLT = _pltRegMap.find(a); + if (regPLT != _pltRegMap.end()) + return regPLT->second; + + // ... and finally prefer to create new compressed PLT entry. + return hasMicroCode ? getPLTMicroEntry(a) : getPLTRegEntry(a); +} + +template <typename ELFT> +void RelocationPass<ELFT>::handlePlain(const MipsELFDefinedAtom<ELFT> &atom, + Reference &ref) { + if (!isDynamic(ref.target())) + return; + + if (requirePLTEntry(ref.target())) + ref.setTarget(getPLTEntry(ref.target())); + else if (requireCopy(ref.target())) + ref.setTarget(getObjectEntry(cast<SharedLibraryAtom>(ref.target()))); +} + +template <typename ELFT> +void RelocationPass<ELFT>::handle26(const MipsELFDefinedAtom<ELFT> &atom, + Reference &ref) { + bool isMicro = ref.kindValue() == R_MICROMIPS_26_S1; + assert((isMicro || ref.kindValue() == R_MIPS_26) && "Unexpected relocation"); + + const auto *sla = dyn_cast<SharedLibraryAtom>(ref.target()); + if (sla && sla->type() == SharedLibraryAtom::Type::Code) + ref.setTarget(isMicro ? getPLTMicroEntry(sla) : getPLTRegEntry(sla)); + + if (requireLA25Stub(ref.target())) + ref.setTarget(getLA25Entry(ref.target(), isMicro)); + + if (!isLocal(ref.target())) { + if (isMicro) + ref.setKindValue(LLD_R_MICROMIPS_GLOBAL_26_S1); + else + ref.setKindValue(LLD_R_MIPS_GLOBAL_26); + } +} + +template <typename ELFT> void RelocationPass<ELFT>::handleGOT(Reference &ref) { + if (!isLocalCall(ref.target())) { + ref.setTarget(getGlobalGOTEntry(ref.target())); + return; + } + + if (ref.kindValue() == R_MIPS_GOT_PAGE) + ref.setTarget(getLocalGOTPageEntry(ref)); + else if (ref.kindValue() == R_MIPS_GOT_DISP) + ref.setTarget(getLocalGOTEntry(ref)); + else if (isLocal(ref.target())) + ref.setTarget(getLocalGOTPageEntry(ref)); + else + ref.setTarget(getLocalGOTEntry(ref)); +} + +template <typename ELFT> +bool RelocationPass<ELFT>::isLocalCall(const Atom *a) const { + Atom::Scope scope; + if (auto *da = dyn_cast<DefinedAtom>(a)) + scope = da->scope(); + else if (auto *aa = dyn_cast<AbsoluteAtom>(a)) + scope = aa->scope(); + else + return false; + + // Local and hidden symbols must be local. + if (scope == Atom::scopeTranslationUnit || scope == Atom::scopeLinkageUnit) + return true; + + // Calls to external symbols defined in an executable file resolved locally. + if (_ctx.getOutputELFType() == ET_EXEC) + return true; + + return false; +} + +template <typename ELFT> +bool RelocationPass<ELFT>::requireLA25Stub(const Atom *a) const { + if (isLocal(a)) + return false; + if (auto *da = dyn_cast<DefinedAtom>(a)) + return static_cast<const MipsELFDefinedAtom<ELFT> *>(da)->file().isPIC(); + return false; +} + +template <typename ELFT> +const GOTAtom *RelocationPass<ELFT>::getLocalGOTEntry(const Reference &ref) { + const Atom *a = ref.target(); + LocalGotMapKeyT key(a, ref.addend()); + + auto got = _gotLocalMap.find(key); + if (got != _gotLocalMap.end()) + return got->second; + + auto ga = new (_file._alloc) GOT0Atom<ELFT>(_file); + _gotLocalMap[key] = ga; + + _localGotVector.push_back(ga); + + Reference::KindValue relKind = ELFT::Is64Bits ? R_MIPS_64 : R_MIPS_32; + ga->addReferenceELF_Mips(relKind, 0, a, 0); + + return ga; +} + +template <typename ELFT> +const GOTAtom * +RelocationPass<ELFT>::getLocalGOTPageEntry(const Reference &ref) { + const Atom *a = ref.target(); + LocalGotMapKeyT key(a, ref.addend()); + + auto got = _gotLocalPageMap.find(key); + if (got != _gotLocalPageMap.end()) + return got->second; + + auto ga = new (_file._alloc) GOT0Atom<ELFT>(_file); + _gotLocalPageMap[key] = ga; + + _localGotVector.push_back(ga); + + Reference::KindValue relKind = + ELFT::Is64Bits ? LLD_R_MIPS_64_HI16 : LLD_R_MIPS_32_HI16; + ga->addReferenceELF_Mips(relKind, 0, a, ref.addend()); + + return ga; +} + +template <typename ELFT> +const GOTAtom *RelocationPass<ELFT>::getGlobalGOTEntry(const Atom *a) { + auto got = _gotGlobalMap.find(a); + if (got != _gotGlobalMap.end()) + return got->second; + + auto ga = new (_file._alloc) GOT0Atom<ELFT>(_file); + _gotGlobalMap[a] = ga; + + _globalGotVector.push_back(ga); + ga->addReferenceELF_Mips(LLD_R_MIPS_GLOBAL_GOT, 0, a, 0); + + if (const DefinedAtom *da = dyn_cast<DefinedAtom>(a)) + ga->addReferenceELF_Mips(R_MIPS_32, 0, da, 0); + + return ga; +} + +template <typename ELFT> +const GOTAtom *RelocationPass<ELFT>::getTLSGOTEntry(const Atom *a) { + auto got = _gotTLSMap.find(a); + if (got != _gotTLSMap.end()) + return got->second; + + auto ga = new (_file._alloc) GOT0Atom<ELFT>(_file); + _gotTLSMap[a] = ga; + + _tlsGotVector.push_back(ga); + Reference::KindValue relKind = + ELFT::Is64Bits ? R_MIPS_TLS_TPREL64 : R_MIPS_TLS_TPREL32; + ga->addReferenceELF_Mips(relKind, 0, a, 0); + + return ga; +} + +template <typename ELFT> +const GOTAtom *RelocationPass<ELFT>::getTLSGdGOTEntry(const Atom *a) { + auto got = _gotTLSGdMap.find(a); + if (got != _gotTLSGdMap.end()) + return got->second; + + auto ga = new (_file._alloc) GOTTLSGdAtom<ELFT>(_file); + _gotTLSGdMap[a] = ga; + + _tlsGotVector.push_back(ga); + if (ELFT::Is64Bits) { + ga->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD64, 0, a, 0); + ga->addReferenceELF_Mips(R_MIPS_TLS_DTPREL64, 8, a, 0); + } else { + ga->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD32, 0, a, 0); + ga->addReferenceELF_Mips(R_MIPS_TLS_DTPREL32, 4, a, 0); + } + + return ga; +} + +template <typename ELFT> +const GOTAtom *RelocationPass<ELFT>::getTLSLdmGOTEntry(const Atom *a) { + if (_gotLDMEntry) + return _gotLDMEntry; + + _gotLDMEntry = new (_file._alloc) GOTTLSGdAtom<ELFT>(_file); + _tlsGotVector.push_back(_gotLDMEntry); + if (ELFT::Is64Bits) + _gotLDMEntry->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD64, 0, _gotLDMEntry, 0); + else + _gotLDMEntry->addReferenceELF_Mips(R_MIPS_TLS_DTPMOD32, 0, _gotLDMEntry, 0); + + return _gotLDMEntry; +} + +template <typename ELFT> +PLTAtom *RelocationPass<ELFT>::createPLTHeader(bool isMicroMips) { + auto ga1 = new (_file._alloc) GOTPLTAtom(_file); + _gotpltVector.insert(_gotpltVector.begin(), ga1); + auto ga0 = new (_file._alloc) GOTPLTAtom(_file); + _gotpltVector.insert(_gotpltVector.begin(), ga0); + + if (isMicroMips) + return new (_file._alloc) PLT0MicroAtom(ga0, _file); + else + return new (_file._alloc) PLT0Atom(ga0, _file); +} + +template <typename ELFT> +const GOTPLTAtom *RelocationPass<ELFT>::getGOTPLTEntry(const Atom *a) { + auto it = _gotpltMap.find(a); + if (it != _gotpltMap.end()) + return it->second; + + auto ga = new (_file._alloc) GOTPLTAtom(a, _file); + _gotpltMap[a] = ga; + _gotpltVector.push_back(ga); + return ga; +} + +template <typename ELFT> +const PLTAtom *RelocationPass<ELFT>::getPLTRegEntry(const Atom *a) { + auto plt = _pltRegMap.find(a); + if (plt != _pltRegMap.end()) + return plt->second; + + PLTAAtom *pa = isR6Target() + ? new (_file._alloc) PLTR6Atom(getGOTPLTEntry(a), _file) + : new (_file._alloc) PLTAAtom(getGOTPLTEntry(a), _file); + _pltRegMap[a] = pa; + _pltRegVector.push_back(pa); + + // Check that 'a' dynamic symbol table record should point to the PLT. + if (_hasStaticRelocations.count(a) && _requiresPtrEquality.count(a)) + pa->addReferenceELF_Mips(LLD_R_MIPS_STO_PLT, 0, a, 0); + + return pa; +} + +template <typename ELFT> +const PLTAtom *RelocationPass<ELFT>::getPLTMicroEntry(const Atom *a) { + auto plt = _pltMicroMap.find(a); + if (plt != _pltMicroMap.end()) + return plt->second; + + auto pa = new (_file._alloc) PLTMicroAtom(getGOTPLTEntry(a), _file); + _pltMicroMap[a] = pa; + _pltMicroVector.push_back(pa); + + // Check that 'a' dynamic symbol table record should point to the PLT. + if (_hasStaticRelocations.count(a) && _requiresPtrEquality.count(a)) + pa->addReferenceELF_Mips(LLD_R_MIPS_STO_PLT, 0, a, 0); + + return pa; +} + +template <typename ELFT> +const LA25Atom *RelocationPass<ELFT>::getLA25RegEntry(const Atom *a) { + auto la25 = _la25RegMap.find(a); + if (la25 != _la25RegMap.end()) + return la25->second; + + auto sa = new (_file._alloc) LA25RegAtom(a, _file); + _la25RegMap[a] = sa; + _la25Vector.push_back(sa); + + return sa; +} + +template <typename ELFT> +const LA25Atom *RelocationPass<ELFT>::getLA25MicroEntry(const Atom *a) { + auto la25 = _la25MicroMap.find(a); + if (la25 != _la25MicroMap.end()) + return la25->second; + + auto sa = new (_file._alloc) LA25MicroAtom(a, _file); + _la25MicroMap[a] = sa; + _la25Vector.push_back(sa); + + return sa; +} + +template <typename ELFT> +const ObjectAtom * +RelocationPass<ELFT>::getObjectEntry(const SharedLibraryAtom *a) { + auto obj = _objectMap.find(a); + if (obj != _objectMap.end()) + return obj->second; + + auto oa = new (_file._alloc) ObjectAtom(_file); + oa->addReferenceELF_Mips(R_MIPS_COPY, 0, oa, 0); + oa->_name = a->name(); + oa->_size = a->size(); + + _objectMap[a] = oa; + _objectVector.push_back(oa); + + return oa; +} + +} // end anon namespace + +static std::unique_ptr<Pass> createPass(MipsLinkingContext &ctx) { + switch (ctx.getTriple().getArch()) { + case llvm::Triple::mipsel: + return llvm::make_unique<RelocationPass<Mips32ELType>>(ctx); + case llvm::Triple::mips64el: + return llvm::make_unique<RelocationPass<Mips64ELType>>(ctx); + default: + llvm_unreachable("Unhandled arch"); + } +} + +std::unique_ptr<Pass> +lld::elf::createMipsRelocationPass(MipsLinkingContext &ctx) { + switch (ctx.getOutputELFType()) { + case ET_EXEC: + case ET_DYN: + return createPass(ctx); + case ET_REL: + return nullptr; + default: + llvm_unreachable("Unhandled output file type"); + } +} diff --git a/lib/ReaderWriter/ELF/Mips/MipsRelocationPass.h b/lib/ReaderWriter/ELF/Mips/MipsRelocationPass.h new file mode 100644 index 0000000000000..af343de5f0276 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsRelocationPass.h @@ -0,0 +1,25 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsRelocationPass.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_MIPS_MIPS_RELOCATION_PASS_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_RELOCATION_PASS_H + +#include <memory> + +namespace lld { +class Pass; + +namespace elf { +class MipsLinkingContext; + +std::unique_ptr<Pass> createMipsRelocationPass(MipsLinkingContext &ctx); + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsSectionChunks.h b/lib/ReaderWriter/ELF/Mips/MipsSectionChunks.h new file mode 100644 index 0000000000000..de9390f2b3074 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsSectionChunks.h @@ -0,0 +1,170 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsSectionChunks.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_MIPS_MIPS_SECTION_CHUNKS_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_SECTION_CHUNKS_H + +namespace lld { +namespace elf { + +template <typename ELFT> class MipsTargetLayout; +class MipsLinkingContext; + +/// \brief Handle Mips GOT section +template <class ELFType> class MipsGOTSection : public AtomSection<ELFType> { +public: + MipsGOTSection(const MipsLinkingContext &ctx) + : AtomSection<ELFType>(ctx, ".got", DefinedAtom::typeGOT, + DefinedAtom::permRW_, + MipsTargetLayout<ELFType>::ORDER_GOT), + _hasNonLocal(false), _localCount(0) { + this->_flags |= SHF_MIPS_GPREL; + this->_alignment = 4; + } + + /// \brief Number of local GOT entries. + std::size_t getLocalCount() const { return _localCount; } + + /// \brief Number of global GOT entries. + std::size_t getGlobalCount() const { return _posMap.size(); } + + /// \brief Does the atom have a global GOT entry? + bool hasGlobalGOTEntry(const Atom *a) const { + return _posMap.count(a) || _tlsMap.count(a); + } + + /// \brief Compare two atoms accordingly theirs positions in the GOT. + bool compare(const Atom *a, const Atom *b) const { + auto ia = _posMap.find(a); + auto ib = _posMap.find(b); + + if (ia != _posMap.end() && ib != _posMap.end()) + return ia->second < ib->second; + + return ia == _posMap.end() && ib != _posMap.end(); + } + + const lld::AtomLayout *appendAtom(const Atom *atom) override { + const DefinedAtom *da = dyn_cast<DefinedAtom>(atom); + + for (const auto &r : *da) { + if (r->kindNamespace() != lld::Reference::KindNamespace::ELF) + continue; + assert(r->kindArch() == Reference::KindArch::Mips); + switch (r->kindValue()) { + case LLD_R_MIPS_GLOBAL_GOT: + _hasNonLocal = true; + _posMap[r->target()] = _posMap.size(); + return AtomSection<ELFType>::appendAtom(atom); + case R_MIPS_TLS_TPREL32: + case R_MIPS_TLS_DTPREL32: + case R_MIPS_TLS_TPREL64: + case R_MIPS_TLS_DTPREL64: + _hasNonLocal = true; + _tlsMap[r->target()] = _tlsMap.size(); + return AtomSection<ELFType>::appendAtom(atom); + case R_MIPS_TLS_DTPMOD32: + case R_MIPS_TLS_DTPMOD64: + _hasNonLocal = true; + break; + } + } + + if (!_hasNonLocal) + ++_localCount; + + return AtomSection<ELFType>::appendAtom(atom); + } + +private: + /// \brief True if the GOT contains non-local entries. + bool _hasNonLocal; + + /// \brief Number of local GOT entries. + std::size_t _localCount; + + /// \brief Map TLS Atoms to their GOT entry index. + llvm::DenseMap<const Atom *, std::size_t> _tlsMap; + + /// \brief Map Atoms to their GOT entry index. + llvm::DenseMap<const Atom *, std::size_t> _posMap; +}; + +/// \brief Handle Mips PLT section +template <class ELFType> class MipsPLTSection : public AtomSection<ELFType> { +public: + MipsPLTSection(const MipsLinkingContext &ctx) + : AtomSection<ELFType>(ctx, ".plt", DefinedAtom::typeGOT, + DefinedAtom::permR_X, + MipsTargetLayout<ELFType>::ORDER_PLT) {} + + const AtomLayout *findPLTLayout(const Atom *plt) const { + auto it = _pltLayoutMap.find(plt); + return it != _pltLayoutMap.end() ? it->second : nullptr; + } + + const lld::AtomLayout *appendAtom(const Atom *atom) override { + const auto *layout = AtomSection<ELFType>::appendAtom(atom); + + const DefinedAtom *da = cast<DefinedAtom>(atom); + + for (const auto &r : *da) { + if (r->kindNamespace() != lld::Reference::KindNamespace::ELF) + continue; + assert(r->kindArch() == Reference::KindArch::Mips); + if (r->kindValue() == LLD_R_MIPS_STO_PLT) { + _pltLayoutMap[r->target()] = layout; + break; + } + } + + return layout; + } + +private: + /// \brief Map PLT Atoms to their layouts. + std::unordered_map<const Atom *, const AtomLayout *> _pltLayoutMap; +}; + +template <class ELFT> class MipsRelocationTable : public RelocationTable<ELFT> { + typedef llvm::object::Elf_Rel_Impl<ELFT, false> Elf_Rel; + typedef llvm::object::Elf_Rel_Impl<ELFT, true> Elf_Rela; + + static const bool _isMips64EL = + ELFT::Is64Bits && ELFT::TargetEndianness == llvm::support::little; + +public: + MipsRelocationTable(const ELFLinkingContext &context, StringRef str, + int32_t order) + : RelocationTable<ELFT>(context, str, order) {} + +protected: + void writeRela(ELFWriter *writer, Elf_Rela &r, const DefinedAtom &atom, + const Reference &ref) override { + uint32_t rType = ref.kindValue() | (ref.tag() << 8); + r.setSymbolAndType(this->getSymbolIndex(ref.target()), rType, _isMips64EL); + r.r_offset = writer->addressOfAtom(&atom) + ref.offsetInAtom(); + // The addend is used only by relative relocations + if (this->_context.isRelativeReloc(ref)) + r.r_addend = writer->addressOfAtom(ref.target()) + ref.addend(); + else + r.r_addend = 0; + } + + void writeRel(ELFWriter *writer, Elf_Rel &r, const DefinedAtom &atom, + const Reference &ref) override { + uint32_t rType = ref.kindValue() | (ref.tag() << 8); + r.setSymbolAndType(this->getSymbolIndex(ref.target()), rType, _isMips64EL); + r.r_offset = writer->addressOfAtom(&atom) + ref.offsetInAtom(); + } +}; + +} // elf +} // lld + +#endif diff --git a/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.cpp b/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.cpp new file mode 100644 index 0000000000000..f60ab63c6af7f --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.cpp @@ -0,0 +1,35 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsTargetHandler.cpp --------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MipsTargetHandler.h" + +using namespace lld; +using namespace elf; + +void MipsRelocationStringTable::registerTable(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, + Reference::KindArch::Mips, kindStrings); +} + +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), + +const Registry::KindStrings MipsRelocationStringTable::kindStrings[] = { +#include "llvm/Support/ELFRelocs/Mips.def" + LLD_KIND_STRING_ENTRY(LLD_R_MIPS_GLOBAL_GOT), + LLD_KIND_STRING_ENTRY(LLD_R_MIPS_32_HI16), + LLD_KIND_STRING_ENTRY(LLD_R_MIPS_64_HI16), + LLD_KIND_STRING_ENTRY(LLD_R_MIPS_GLOBAL_26), + LLD_KIND_STRING_ENTRY(LLD_R_MIPS_HI16), + LLD_KIND_STRING_ENTRY(LLD_R_MIPS_LO16), + LLD_KIND_STRING_ENTRY(LLD_R_MIPS_STO_PLT), + LLD_KIND_STRING_ENTRY(LLD_R_MICROMIPS_GLOBAL_26_S1), + LLD_KIND_STRING_END +}; + +#undef ELF_RELOC diff --git a/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h b/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h new file mode 100644 index 0000000000000..79509addf40b4 --- /dev/null +++ b/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h @@ -0,0 +1,257 @@ +//===- lib/ReaderWriter/ELF/Mips/MipsTargetHandler.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_MIPS_MIPS_TARGET_HANDLER_H +#define LLD_READER_WRITER_ELF_MIPS_MIPS_TARGET_HANDLER_H + +#include "DefaultTargetHandler.h" +#include "MipsDynamicLibraryWriter.h" +#include "MipsELFReader.h" +#include "MipsExecutableWriter.h" +#include "MipsLinkingContext.h" +#include "MipsRelocationHandler.h" +#include "MipsSectionChunks.h" +#include "TargetLayout.h" +#include "llvm/ADT/DenseSet.h" + +namespace lld { +namespace elf { + +/// \brief TargetLayout for Mips +template <class ELFT> class MipsTargetLayout final : public TargetLayout<ELFT> { +public: + MipsTargetLayout(MipsLinkingContext &ctx) + : TargetLayout<ELFT>(ctx), + _gotSection(new (this->_allocator) MipsGOTSection<ELFT>(ctx)), + _pltSection(new (this->_allocator) MipsPLTSection<ELFT>(ctx)) {} + + const MipsGOTSection<ELFT> &getGOTSection() const { return *_gotSection; } + const MipsPLTSection<ELFT> &getPLTSection() const { return *_pltSection; } + + AtomSection<ELFT> *createSection(StringRef name, int32_t type, + DefinedAtom::ContentPermissions permissions, + Layout::SectionOrder order) override { + if (type == DefinedAtom::typeGOT && name == ".got") + return _gotSection; + if (type == DefinedAtom::typeStub && name == ".plt") + return _pltSection; + return DefaultLayout<ELFT>::createSection(name, type, permissions, order); + } + + /// \brief GP offset relative to .got section. + uint64_t getGPOffset() const { return 0x7FF0; } + + /// \brief Get '_gp' symbol atom layout. + AtomLayout *getGP() { + if (!_gpAtom.hasValue()) { + auto atom = this->findAbsoluteAtom("_gp"); + _gpAtom = atom != this->absoluteAtoms().end() ? *atom : nullptr; + } + return *_gpAtom; + } + + /// \brief Get '_gp_disp' symbol atom layout. + AtomLayout *getGPDisp() { + if (!_gpDispAtom.hasValue()) { + auto atom = this->findAbsoluteAtom("_gp_disp"); + _gpDispAtom = atom != this->absoluteAtoms().end() ? *atom : nullptr; + } + return *_gpDispAtom; + } + + /// \brief Return the section order for a input section + Layout::SectionOrder getSectionOrder(StringRef name, int32_t contentType, + int32_t contentPermissions) override { + if ((contentType == DefinedAtom::typeStub) && (name.startswith(".text"))) + return DefaultLayout<ELFT>::ORDER_TEXT; + + return DefaultLayout<ELFT>::getSectionOrder(name, contentType, + contentPermissions); + } + +protected: + unique_bump_ptr<RelocationTable<ELFT>> + createRelocationTable(StringRef name, int32_t order) override { + return unique_bump_ptr<RelocationTable<ELFT>>( + new (this->_allocator) + MipsRelocationTable<ELFT>(this->_context, name, order)); + } + +private: + MipsGOTSection<ELFT> *_gotSection; + MipsPLTSection<ELFT> *_pltSection; + llvm::Optional<AtomLayout *> _gpAtom; + llvm::Optional<AtomLayout *> _gpDispAtom; +}; + +/// \brief Mips Runtime file. +template <class ELFT> class MipsRuntimeFile final : public RuntimeFile<ELFT> { +public: + MipsRuntimeFile(MipsLinkingContext &ctx) + : RuntimeFile<ELFT>(ctx, "Mips runtime file") {} +}; + +/// \brief Auxiliary class holds relocation's names table. +class MipsRelocationStringTable { + static const Registry::KindStrings kindStrings[]; + +public: + static void registerTable(Registry ®istry); +}; + +/// \brief TargetHandler for Mips +template <class ELFT> +class MipsTargetHandler final : public DefaultTargetHandler<ELFT> { +public: + MipsTargetHandler(MipsLinkingContext &ctx) + : _ctx(ctx), _runtimeFile(new MipsRuntimeFile<ELFT>(ctx)), + _targetLayout(new MipsTargetLayout<ELFT>(ctx)), + _relocationHandler(createMipsRelocationHandler<ELFT>(ctx)) {} + + MipsTargetLayout<ELFT> &getTargetLayout() override { return *_targetLayout; } + + std::unique_ptr<Reader> getObjReader() override { + return std::unique_ptr<Reader>(new MipsELFObjectReader<ELFT>(_ctx)); + } + + std::unique_ptr<Reader> getDSOReader() override { + return std::unique_ptr<Reader>(new MipsELFDSOReader<ELFT>(_ctx)); + } + + const TargetRelocationHandler &getRelocationHandler() const override { + return *_relocationHandler; + } + + std::unique_ptr<Writer> getWriter() override { + switch (_ctx.getOutputELFType()) { + case llvm::ELF::ET_EXEC: + return std::unique_ptr<Writer>( + new MipsExecutableWriter<ELFT>(_ctx, *_targetLayout)); + case llvm::ELF::ET_DYN: + return std::unique_ptr<Writer>( + new MipsDynamicLibraryWriter<ELFT>(_ctx, *_targetLayout)); + case llvm::ELF::ET_REL: + llvm_unreachable("TODO: support -r mode"); + default: + llvm_unreachable("unsupported output type"); + } + } + + void registerRelocationNames(Registry ®istry) override { + MipsRelocationStringTable::registerTable(registry); + } + +private: + MipsLinkingContext &_ctx; + std::unique_ptr<MipsRuntimeFile<ELFT>> _runtimeFile; + std::unique_ptr<MipsTargetLayout<ELFT>> _targetLayout; + std::unique_ptr<TargetRelocationHandler> _relocationHandler; +}; + +template <class ELFT> class MipsSymbolTable : public SymbolTable<ELFT> { +public: + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + + MipsSymbolTable(const ELFLinkingContext &ctx) + : SymbolTable<ELFT>(ctx, ".symtab", + DefaultLayout<ELFT>::ORDER_SYMBOL_TABLE) {} + + void addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, + int64_t addr) override { + SymbolTable<ELFT>::addDefinedAtom(sym, da, addr); + + switch (da->codeModel()) { + case DefinedAtom::codeMipsMicro: + sym.st_other |= llvm::ELF::STO_MIPS_MICROMIPS; + break; + case DefinedAtom::codeMipsMicroPIC: + sym.st_other |= llvm::ELF::STO_MIPS_MICROMIPS | llvm::ELF::STO_MIPS_PIC; + break; + default: + break; + } + } + + void finalize(bool sort) override { + SymbolTable<ELFT>::finalize(sort); + + for (auto &ste : this->_symbolTable) { + if (!ste._atom) + continue; + if (const auto *da = dyn_cast<DefinedAtom>(ste._atom)) { + if (da->codeModel() == DefinedAtom::codeMipsMicro || + da->codeModel() == DefinedAtom::codeMipsMicroPIC) { + // Adjust dynamic microMIPS symbol value. That allows a dynamic + // linker to recognize and handle this symbol correctly. + ste._symbol.st_value = ste._symbol.st_value | 1; + } + } + } + } +}; + +template <class ELFT> +class MipsDynamicSymbolTable : public DynamicSymbolTable<ELFT> { +public: + MipsDynamicSymbolTable(const ELFLinkingContext &ctx, + MipsTargetLayout<ELFT> &layout) + : DynamicSymbolTable<ELFT>(ctx, layout, ".dynsym", + DefaultLayout<ELFT>::ORDER_DYNAMIC_SYMBOLS), + _targetLayout(layout) {} + + void sortSymbols() override { + typedef typename DynamicSymbolTable<ELFT>::SymbolEntry SymbolEntry; + std::stable_sort(this->_symbolTable.begin(), this->_symbolTable.end(), + [this](const SymbolEntry &A, const SymbolEntry &B) { + if (A._symbol.getBinding() != STB_GLOBAL && + B._symbol.getBinding() != STB_GLOBAL) + return A._symbol.getBinding() < B._symbol.getBinding(); + + return _targetLayout.getGOTSection().compare(A._atom, B._atom); + }); + } + + void finalize() override { + DynamicSymbolTable<ELFT>::finalize(); + + const auto &pltSection = _targetLayout.getPLTSection(); + + for (auto &ste : this->_symbolTable) { + const Atom *a = ste._atom; + if (!a) + continue; + if (auto *layout = pltSection.findPLTLayout(a)) { + a = layout->_atom; + // Under some conditions a dynamic symbol table record should hold + // a symbol value of the corresponding PLT entry. For details look + // at the PLT entry creation code in the class MipsRelocationPass. + // Let's update atomLayout fields for such symbols. + assert(!ste._atomLayout); + ste._symbol.st_value = layout->_virtualAddr; + ste._symbol.st_other |= ELF::STO_MIPS_PLT; + } + + if (const auto *da = dyn_cast<DefinedAtom>(a)) { + if (da->codeModel() == DefinedAtom::codeMipsMicro || + da->codeModel() == DefinedAtom::codeMipsMicroPIC) { + // Adjust dynamic microMIPS symbol value. That allows a dynamic + // linker to recognize and handle this symbol correctly. + ste._symbol.st_value = ste._symbol.st_value | 1; + } + } + } + } + +private: + MipsTargetLayout<ELFT> &_targetLayout; +}; + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/OrderPass.h b/lib/ReaderWriter/ELF/OrderPass.h new file mode 100644 index 0000000000000..d126b830db963 --- /dev/null +++ b/lib/ReaderWriter/ELF/OrderPass.h @@ -0,0 +1,30 @@ +//===- lib/ReaderWriter/ELF/OrderPass.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_ORDER_PASS_H +#define LLD_READER_WRITER_ELF_ORDER_PASS_H + +#include "lld/Core/Parallel.h" +#include <limits> + +namespace lld { +namespace elf { + +/// \brief This pass sorts atoms by file and atom ordinals. +class OrderPass : public Pass { +public: + void perform(std::unique_ptr<MutableFile> &file) override { + parallel_sort(file->definedAtoms().begin(), file->definedAtoms().end(), + DefinedAtom::compareByPosition); + } +}; +} +} + +#endif diff --git a/lib/ReaderWriter/ELF/OutputELFWriter.h b/lib/ReaderWriter/ELF/OutputELFWriter.h new file mode 100644 index 0000000000000..c137905b936b6 --- /dev/null +++ b/lib/ReaderWriter/ELF/OutputELFWriter.h @@ -0,0 +1,615 @@ +//===- lib/ReaderWriter/ELF/OutputELFWriter.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_OUTPUT_WRITER_H +#define LLD_READER_WRITER_ELF_OUTPUT_WRITER_H + +#include "DefaultLayout.h" +#include "ELFFile.h" +#include "TargetLayout.h" +#include "lld/Core/Instrumentation.h" +#include "lld/Core/Parallel.h" +#include "lld/Core/SharedLibraryFile.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "lld/Core/Simple.h" +#include "lld/Core/Writer.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/Path.h" + +namespace lld { +namespace elf { +using namespace llvm; +using namespace llvm::object; + +template <class ELFT> class OutputELFWriter; +template <class ELFT> class TargetLayout; + +namespace { + +template<class ELFT> +class SymbolFile : public RuntimeFile<ELFT> { +public: + SymbolFile(ELFLinkingContext &context) + : RuntimeFile<ELFT>(context, "Dynamic absolute symbols"), + _atomsAdded(false) {} + + Atom *addAbsoluteAtom(StringRef symbolName) override { + auto *a = RuntimeFile<ELFT>::addAbsoluteAtom(symbolName); + if (a) _atomsAdded = true; + return a; + } + + Atom *addUndefinedAtom(StringRef) override { + llvm_unreachable("Cannot add undefined atoms to resolve undefined symbols"); + } + + bool hasAtoms() const { return _atomsAdded; } + +private: + bool _atomsAdded; +}; + +template<class ELFT> +class DynamicSymbolFile : public SimpleArchiveLibraryFile { + typedef std::function<void(StringRef, RuntimeFile<ELFT> &)> Resolver; +public: + DynamicSymbolFile(ELFLinkingContext &context, Resolver resolver) + : SimpleArchiveLibraryFile("Dynamically added runtime symbols"), + _context(context), _resolver(resolver) {} + + File *find(StringRef sym, bool dataSymbolOnly) override { + if (!_file) + _file.reset(new (_alloc) SymbolFile<ELFT>(_context)); + + assert(!_file->hasAtoms() && "The file shouldn't have atoms yet"); + _resolver(sym, *_file); + // If atoms were added - release the file to the caller. + return _file->hasAtoms() ? _file.release() : nullptr; + } + +private: + ELFLinkingContext &_context; + Resolver _resolver; + + // The allocator should go before bump pointers because of + // reversed destruction order. + llvm::BumpPtrAllocator _alloc; + unique_bump_ptr<SymbolFile<ELFT>> _file; +}; + +} // end anon namespace + +//===----------------------------------------------------------------------===// +// OutputELFWriter Class +//===----------------------------------------------------------------------===// +/// \brief This acts as the base class for all the ELF writers that are output +/// for emitting an ELF output file. This class also acts as a common class for +/// creating static and dynamic executables. All the function in this class +/// can be overridden and an appropriate writer be created +template<class ELFT> +class OutputELFWriter : public ELFWriter { +public: + typedef Elf_Shdr_Impl<ELFT> Elf_Shdr; + typedef Elf_Sym_Impl<ELFT> Elf_Sym; + typedef Elf_Dyn_Impl<ELFT> Elf_Dyn; + + OutputELFWriter(ELFLinkingContext &context, TargetLayout<ELFT> &layout); + +protected: + // build the sections that need to be created + virtual void createDefaultSections(); + + // Build all the output sections + void buildChunks(const File &file) override; + + // Build the output file + virtual std::error_code buildOutput(const File &file); + + // Setup the ELF header. + virtual std::error_code setELFHeader(); + + // Write the file to the path specified + std::error_code writeFile(const File &File, StringRef path) override; + + // Write to the output file. + virtual std::error_code writeOutput(const File &file, StringRef path); + + // Get the size of the output file that the linker would emit. + virtual uint64_t outputFileSize() const; + + // Build the atom to address map, this has to be called + // before applying relocations + virtual void buildAtomToAddressMap(const File &file); + + // Build the symbol table for static linking + virtual void buildStaticSymbolTable(const File &file); + + // Build the dynamic symbol table for dynamic linking + virtual void buildDynamicSymbolTable(const File &file); + + // Build the section header table + virtual void buildSectionHeaderTable(); + + // Assign sections that have no segments such as the symbol table, + // section header table, string table etc + virtual void assignSectionsWithNoSegments(); + + // Add default atoms that need to be present in the output file + virtual void addDefaultAtoms(); + + // Add any runtime files and their atoms to the output + bool createImplicitFiles(std::vector<std::unique_ptr<File>> &) override; + + // Finalize the default atom values + virtual void finalizeDefaultAtomValues(); + + // This is called by the write section to apply relocations + uint64_t addressOfAtom(const Atom *atom) override { + auto addr = _atomToAddressMap.find(atom); + return addr == _atomToAddressMap.end() ? 0 : addr->second; + } + + // This is a hook for creating default dynamic entries + virtual void createDefaultDynamicEntries() {} + + /// \brief Create symbol table. + virtual unique_bump_ptr<SymbolTable<ELFT>> createSymbolTable(); + + /// \brief create dynamic table. + virtual unique_bump_ptr<DynamicTable<ELFT>> createDynamicTable(); + + /// \brief create dynamic symbol table. + virtual unique_bump_ptr<DynamicSymbolTable<ELFT>> + createDynamicSymbolTable(); + + /// \brief Create entry in the dynamic symbols table for this atom. + virtual bool isDynSymEntryRequired(const SharedLibraryAtom *sla) const { + return _layout.isReferencedByDefinedAtom(sla); + } + + /// \brief Create DT_NEEDED dynamic tage for the shared library. + virtual bool isNeededTagRequired(const SharedLibraryAtom *sla) const { + return false; + } + + /// \brief Process undefined symbols that left after resolution step. + virtual void processUndefinedSymbol(StringRef symName, + RuntimeFile<ELFT> &file) const {} + + llvm::BumpPtrAllocator _alloc; + + ELFLinkingContext &_context; + TargetHandler<ELFT> &_targetHandler; + + typedef llvm::DenseMap<const Atom *, uint64_t> AtomToAddress; + AtomToAddress _atomToAddressMap; + TargetLayout<ELFT> &_layout; + unique_bump_ptr<ELFHeader<ELFT>> _elfHeader; + unique_bump_ptr<ProgramHeader<ELFT>> _programHeader; + unique_bump_ptr<SymbolTable<ELFT>> _symtab; + unique_bump_ptr<StringTable<ELFT>> _strtab; + unique_bump_ptr<StringTable<ELFT>> _shstrtab; + unique_bump_ptr<SectionHeader<ELFT>> _shdrtab; + unique_bump_ptr<EHFrameHeader<ELFT>> _ehFrameHeader; + /// \name Dynamic sections. + /// @{ + unique_bump_ptr<DynamicTable<ELFT>> _dynamicTable; + unique_bump_ptr<DynamicSymbolTable<ELFT>> _dynamicSymbolTable; + unique_bump_ptr<StringTable<ELFT>> _dynamicStringTable; + unique_bump_ptr<HashSection<ELFT>> _hashTable; + llvm::StringSet<> _soNeeded; + /// @} + std::unique_ptr<RuntimeFile<ELFT>> _scriptFile; + +private: + static StringRef maybeGetSOName(Node *node); +}; + +//===----------------------------------------------------------------------===// +// OutputELFWriter +//===----------------------------------------------------------------------===// +template <class ELFT> +OutputELFWriter<ELFT>::OutputELFWriter(ELFLinkingContext &context, + TargetLayout<ELFT> &layout) + : _context(context), _targetHandler(context.getTargetHandler<ELFT>()), + _layout(layout), + _scriptFile(new RuntimeFile<ELFT>(context, "Linker script runtime")) {} + +template <class ELFT> +void OutputELFWriter<ELFT>::buildChunks(const File &file) { + ScopedTask task(getDefaultDomain(), "buildChunks"); + for (const DefinedAtom *definedAtom : file.defined()) { + DefinedAtom::ContentType contentType = definedAtom->contentType(); + // Dont add COMDAT group atoms and GNU linkonce atoms, as they are used for + // symbol resolution. + // TODO: handle partial linking. + if (contentType == DefinedAtom::typeGroupComdat || + contentType == DefinedAtom::typeGnuLinkOnce) + continue; + _layout.addAtom(definedAtom); + } + for (const AbsoluteAtom *absoluteAtom : file.absolute()) + _layout.addAtom(absoluteAtom); +} + +template <class ELFT> +void OutputELFWriter<ELFT>::buildStaticSymbolTable(const File &file) { + ScopedTask task(getDefaultDomain(), "buildStaticSymbolTable"); + for (auto sec : _layout.sections()) + if (auto section = dyn_cast<AtomSection<ELFT>>(sec)) + for (const auto &atom : section->atoms()) + _symtab->addSymbol(atom->_atom, section->ordinal(), atom->_virtualAddr); + for (auto &atom : _layout.absoluteAtoms()) + _symtab->addSymbol(atom->_atom, ELF::SHN_ABS, atom->_virtualAddr); + for (const UndefinedAtom *a : file.undefined()) + _symtab->addSymbol(a, ELF::SHN_UNDEF); +} + +// Returns the DSO name for a given input file if it's a shared library +// file and not marked as --as-needed. +template <class ELFT> +StringRef OutputELFWriter<ELFT>::maybeGetSOName(Node *node) { + if (auto *fnode = dyn_cast<FileNode>(node)) + if (!fnode->asNeeded()) + if (auto *file = dyn_cast<SharedLibraryFile>(fnode->getFile())) + return file->getDSOName(); + return ""; +} + +template <class ELFT> +void OutputELFWriter<ELFT>::buildDynamicSymbolTable(const File &file) { + ScopedTask task(getDefaultDomain(), "buildDynamicSymbolTable"); + for (const auto &sla : file.sharedLibrary()) { + if (isDynSymEntryRequired(sla)) { + _dynamicSymbolTable->addSymbol(sla, ELF::SHN_UNDEF); + _soNeeded.insert(sla->loadName()); + continue; + } + if (isNeededTagRequired(sla)) + _soNeeded.insert(sla->loadName()); + } + for (const std::unique_ptr<Node> &node : _context.getNodes()) { + StringRef soname = maybeGetSOName(node.get()); + if (!soname.empty()) + _soNeeded.insert(soname); + } + // Never mark the dynamic linker as DT_NEEDED + _soNeeded.erase(sys::path::filename(_context.getInterpreter())); + for (const auto &loadName : _soNeeded) { + Elf_Dyn dyn; + dyn.d_tag = DT_NEEDED; + dyn.d_un.d_val = _dynamicStringTable->addString(loadName.getKey()); + _dynamicTable->addEntry(dyn); + } + const auto &rpathList = _context.getRpathList(); + if (!rpathList.empty()) { + auto rpath = new (_alloc) std::string(join(rpathList.begin(), + rpathList.end(), ":")); + Elf_Dyn dyn; + dyn.d_tag = DT_RPATH; + dyn.d_un.d_val = _dynamicStringTable->addString(*rpath); + _dynamicTable->addEntry(dyn); + } + StringRef soname = _context.sharedObjectName(); + if (!soname.empty() && _context.getOutputELFType() == llvm::ELF::ET_DYN) { + Elf_Dyn dyn; + dyn.d_tag = DT_SONAME; + dyn.d_un.d_val = _dynamicStringTable->addString(soname); + _dynamicTable->addEntry(dyn); + } + // The dynamic symbol table need to be sorted earlier because the hash + // table needs to be built using the dynamic symbol table. It would be + // late to sort the symbols due to that in finalize. In the dynamic symbol + // table finalize, we call the symbol table finalize and we don't want to + // sort again + _dynamicSymbolTable->sortSymbols(); + + // Add the dynamic symbols into the hash table + _dynamicSymbolTable->addSymbolsToHashTable(); +} + +template <class ELFT> +void OutputELFWriter<ELFT>::buildAtomToAddressMap(const File &file) { + ScopedTask task(getDefaultDomain(), "buildAtomToAddressMap"); + int64_t totalAbsAtoms = _layout.absoluteAtoms().size(); + int64_t totalUndefinedAtoms = file.undefined().size(); + int64_t totalDefinedAtoms = 0; + for (auto sec : _layout.sections()) + if (auto section = dyn_cast<AtomSection<ELFT> >(sec)) { + totalDefinedAtoms += section->atoms().size(); + for (const auto &atom : section->atoms()) + _atomToAddressMap[atom->_atom] = atom->_virtualAddr; + } + // build the atomToAddressMap that contains absolute symbols too + for (auto &atom : _layout.absoluteAtoms()) + _atomToAddressMap[atom->_atom] = atom->_virtualAddr; + + // Set the total number of atoms in the symbol table, so that appropriate + // resizing of the string table can be done + _symtab->setNumEntries(totalDefinedAtoms + totalAbsAtoms + + totalUndefinedAtoms); +} + +template<class ELFT> +void OutputELFWriter<ELFT>::buildSectionHeaderTable() { + ScopedTask task(getDefaultDomain(), "buildSectionHeaderTable"); + for (auto outputSection : _layout.outputSections()) { + if (outputSection->kind() != Chunk<ELFT>::Kind::ELFSection && + outputSection->kind() != Chunk<ELFT>::Kind::AtomSection) + continue; + if (outputSection->hasSegment()) + _shdrtab->appendSection(outputSection); + } +} + +template<class ELFT> +void OutputELFWriter<ELFT>::assignSectionsWithNoSegments() { + ScopedTask task(getDefaultDomain(), "assignSectionsWithNoSegments"); + for (auto outputSection : _layout.outputSections()) { + if (outputSection->kind() != Chunk<ELFT>::Kind::ELFSection && + outputSection->kind() != Chunk<ELFT>::Kind::AtomSection) + continue; + if (!outputSection->hasSegment()) + _shdrtab->appendSection(outputSection); + } + _layout.assignFileOffsetsForMiscSections(); + for (auto sec : _layout.sections()) + if (auto section = dyn_cast<Section<ELFT>>(sec)) + if (!DefaultLayout<ELFT>::hasOutputSegment(section)) + _shdrtab->updateSection(section); +} + +template <class ELFT> void OutputELFWriter<ELFT>::addDefaultAtoms() { + const llvm::StringSet<> &symbols = + _context.linkerScriptSema().getScriptDefinedSymbols(); + for (auto &sym : symbols) + _scriptFile->addAbsoluteAtom(sym.getKey()); +} + +template <class ELFT> +bool OutputELFWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + // Add the virtual archive to resolve undefined symbols. + // The file will be added later in the linking context. + auto callback = [this](StringRef sym, RuntimeFile<ELFT> &file) { + processUndefinedSymbol(sym, file); + }; + auto &ctx = const_cast<ELFLinkingContext &>(_context); + ctx.setUndefinesResolver( + llvm::make_unique<DynamicSymbolFile<ELFT>>(ctx, std::move(callback))); + // Add script defined symbols + result.push_back(std::move(_scriptFile)); + return true; +} + +template <class ELFT> +void OutputELFWriter<ELFT>::finalizeDefaultAtomValues() { + const llvm::StringSet<> &symbols = + _context.linkerScriptSema().getScriptDefinedSymbols(); + for (auto &sym : symbols) { + uint64_t res = + _context.linkerScriptSema().getLinkerScriptExprValue(sym.getKey()); + auto a = _layout.findAbsoluteAtom(sym.getKey()); + (*a)->_virtualAddr = res; + } +} + +template <class ELFT> void OutputELFWriter<ELFT>::createDefaultSections() { + _elfHeader.reset(new (_alloc) ELFHeader<ELFT>(_context)); + _programHeader.reset(new (_alloc) ProgramHeader<ELFT>(_context)); + _layout.setHeader(_elfHeader.get()); + _layout.setProgramHeader(_programHeader.get()); + + _symtab = std::move(this->createSymbolTable()); + _strtab.reset(new (_alloc) StringTable<ELFT>( + _context, ".strtab", DefaultLayout<ELFT>::ORDER_STRING_TABLE)); + _shstrtab.reset(new (_alloc) StringTable<ELFT>( + _context, ".shstrtab", DefaultLayout<ELFT>::ORDER_SECTION_STRINGS)); + _shdrtab.reset(new (_alloc) SectionHeader<ELFT>( + _context, DefaultLayout<ELFT>::ORDER_SECTION_HEADERS)); + _layout.addSection(_symtab.get()); + _layout.addSection(_strtab.get()); + _layout.addSection(_shstrtab.get()); + _shdrtab->setStringSection(_shstrtab.get()); + _symtab->setStringSection(_strtab.get()); + _layout.addSection(_shdrtab.get()); + + for (auto sec : _layout.sections()) { + // TODO: use findOutputSection + auto section = dyn_cast<Section<ELFT>>(sec); + if (!section || section->outputSectionName() != ".eh_frame") + continue; + _ehFrameHeader.reset(new (_alloc) EHFrameHeader<ELFT>( + _context, ".eh_frame_hdr", _layout, + DefaultLayout<ELFT>::ORDER_EH_FRAMEHDR)); + _layout.addSection(_ehFrameHeader.get()); + break; + } + + if (_context.isDynamic()) { + _dynamicTable = std::move(createDynamicTable()); + _dynamicStringTable.reset(new (_alloc) StringTable<ELFT>( + _context, ".dynstr", DefaultLayout<ELFT>::ORDER_DYNAMIC_STRINGS, true)); + _dynamicSymbolTable = std::move(createDynamicSymbolTable()); + _hashTable.reset(new (_alloc) HashSection<ELFT>( + _context, ".hash", DefaultLayout<ELFT>::ORDER_HASH)); + // Set the hash table in the dynamic symbol table so that the entries in the + // hash table can be created + _dynamicSymbolTable->setHashTable(_hashTable.get()); + _hashTable->setSymbolTable(_dynamicSymbolTable.get()); + _layout.addSection(_dynamicTable.get()); + _layout.addSection(_dynamicStringTable.get()); + _layout.addSection(_dynamicSymbolTable.get()); + _layout.addSection(_hashTable.get()); + _dynamicSymbolTable->setStringSection(_dynamicStringTable.get()); + _dynamicTable->setSymbolTable(_dynamicSymbolTable.get()); + _dynamicTable->setHashTable(_hashTable.get()); + if (_layout.hasDynamicRelocationTable()) + _layout.getDynamicRelocationTable()->setSymbolTable( + _dynamicSymbolTable.get()); + if (_layout.hasPLTRelocationTable()) + _layout.getPLTRelocationTable()->setSymbolTable( + _dynamicSymbolTable.get()); + } +} + +template <class ELFT> +unique_bump_ptr<SymbolTable<ELFT>> + OutputELFWriter<ELFT>::createSymbolTable() { + return unique_bump_ptr<SymbolTable<ELFT>>(new (_alloc) SymbolTable<ELFT>( + this->_context, ".symtab", DefaultLayout<ELFT>::ORDER_SYMBOL_TABLE)); +} + +/// \brief create dynamic table +template <class ELFT> +unique_bump_ptr<DynamicTable<ELFT>> + OutputELFWriter<ELFT>::createDynamicTable() { + return unique_bump_ptr<DynamicTable<ELFT>>( + new (_alloc) DynamicTable<ELFT>( + this->_context, _layout, ".dynamic", DefaultLayout<ELFT>::ORDER_DYNAMIC)); +} + +/// \brief create dynamic symbol table +template <class ELFT> +unique_bump_ptr<DynamicSymbolTable<ELFT>> + OutputELFWriter<ELFT>::createDynamicSymbolTable() { + return unique_bump_ptr<DynamicSymbolTable<ELFT>>( + new (_alloc) DynamicSymbolTable<ELFT>( + this->_context, _layout, ".dynsym", + DefaultLayout<ELFT>::ORDER_DYNAMIC_SYMBOLS)); +} + +template <class ELFT> +std::error_code OutputELFWriter<ELFT>::buildOutput(const File &file) { + ScopedTask buildTask(getDefaultDomain(), "ELF Writer buildOutput"); + buildChunks(file); + + // Create the default sections like the symbol table, string table, and the + // section string table + createDefaultSections(); + + // Set the Layout + _layout.assignSectionsToSegments(); + + // Create the dynamic table entries + if (_context.isDynamic()) { + _dynamicTable->createDefaultEntries(); + buildDynamicSymbolTable(file); + } + + // Call the preFlight callbacks to modify the sections and the atoms + // contained in them, in anyway the targets may want + _layout.doPreFlight(); + + _layout.assignVirtualAddress(); + + // Finalize the default value of symbols that the linker adds + finalizeDefaultAtomValues(); + + // Build the Atom To Address map for applying relocations + buildAtomToAddressMap(file); + + // Create symbol table and section string table + // Do it only if -s is not specified. + if (!_context.stripSymbols()) + buildStaticSymbolTable(file); + + // Finalize the layout by calling the finalize() functions + _layout.finalize(); + + // build Section Header table + buildSectionHeaderTable(); + + // assign Offsets and virtual addresses + // for sections with no segments + assignSectionsWithNoSegments(); + + if (_context.isDynamic()) + _dynamicTable->updateDynamicTable(); + + return std::error_code(); +} + +template <class ELFT> std::error_code OutputELFWriter<ELFT>::setELFHeader() { + _elfHeader->e_type(_context.getOutputELFType()); + _elfHeader->e_machine(_context.getOutputMachine()); + _elfHeader->e_ident(ELF::EI_VERSION, 1); + _elfHeader->e_ident(ELF::EI_OSABI, 0); + _elfHeader->e_version(1); + _elfHeader->e_phoff(_programHeader->fileOffset()); + _elfHeader->e_shoff(_shdrtab->fileOffset()); + _elfHeader->e_phentsize(_programHeader->entsize()); + _elfHeader->e_phnum(_programHeader->numHeaders()); + _elfHeader->e_shentsize(_shdrtab->entsize()); + _elfHeader->e_shnum(_shdrtab->numHeaders()); + _elfHeader->e_shstrndx(_shstrtab->ordinal()); + if (const auto *al = _layout.findAtomLayoutByName(_context.entrySymbolName())) + _elfHeader->e_entry(al->_virtualAddr); + else + _elfHeader->e_entry(0); + + return std::error_code(); +} + +template <class ELFT> uint64_t OutputELFWriter<ELFT>::outputFileSize() const { + return _shdrtab->fileOffset() + _shdrtab->fileSize(); +} + +template <class ELFT> +std::error_code OutputELFWriter<ELFT>::writeOutput(const File &file, + StringRef path) { + std::unique_ptr<FileOutputBuffer> buffer; + ScopedTask createOutputTask(getDefaultDomain(), "ELF Writer Create Output"); + std::error_code ec = FileOutputBuffer::create(path, outputFileSize(), buffer, + FileOutputBuffer::F_executable); + createOutputTask.end(); + + if (ec) + return ec; + + ScopedTask writeTask(getDefaultDomain(), "ELF Writer write to memory"); + + // HACK: We have to write out the header and program header here even though + // they are a member of a segment because only sections are written in the + // following loop. + + // Finalize ELF Header / Program Headers. + _elfHeader->finalize(); + _programHeader->finalize(); + + _elfHeader->write(this, _layout, *buffer); + _programHeader->write(this, _layout, *buffer); + + auto sections = _layout.sections(); + parallel_for_each( + sections.begin(), sections.end(), + [&](Chunk<ELFT> *section) { section->write(this, _layout, *buffer); }); + writeTask.end(); + + ScopedTask commitTask(getDefaultDomain(), "ELF Writer commit to disk"); + return buffer->commit(); +} + +template <class ELFT> +std::error_code OutputELFWriter<ELFT>::writeFile(const File &file, + StringRef path) { + std::error_code ec = buildOutput(file); + if (ec) + return ec; + + ec = setELFHeader(); + if (ec) + return ec; + + return writeOutput(file, path); +} +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_ELF_OUTPUT_WRITER_H diff --git a/lib/ReaderWriter/ELF/Reader.cpp b/lib/ReaderWriter/ELF/Reader.cpp new file mode 100644 index 0000000000000..fc113d4789132 --- /dev/null +++ b/lib/ReaderWriter/ELF/Reader.cpp @@ -0,0 +1,43 @@ +//===- lib/ReaderWriter/ELF/Reader.cpp ------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Defines the ELF Reader and all helper sub classes to consume an ELF +/// file and produces atoms out of it. +/// +//===----------------------------------------------------------------------===// + +#include "ELFReader.h" +#include <map> +#include <vector> + +using llvm::support::endianness; +using namespace llvm::object; + +namespace lld { + +// This dynamic registration of a handler causes support for all ELF +// architectures to be pulled into the linker. If we want to support making a +// linker that only supports one ELF architecture, we'd need to change this +// to have a different registration method for each architecture. +void Registry::addSupportELFObjects(ELFLinkingContext &ctx) { + + // Tell registry about the ELF object file parser. + add(std::move(ctx.targetHandler()->getObjReader())); + + // Tell registry about the relocation name to number mapping for this arch. + ctx.targetHandler()->registerRelocationNames(*this); +} + +void Registry::addSupportELFDynamicSharedObjects(ELFLinkingContext &ctx) { + // Tell registry about the ELF dynamic shared library file parser. + add(ctx.targetHandler()->getDSOReader()); +} + +} // end namespace lld diff --git a/lib/ReaderWriter/ELF/SectionChunks.h b/lib/ReaderWriter/ELF/SectionChunks.h new file mode 100644 index 0000000000000..03bdb59e65681 --- /dev/null +++ b/lib/ReaderWriter/ELF/SectionChunks.h @@ -0,0 +1,1498 @@ +//===- lib/ReaderWriter/ELF/SectionChunks.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_SECTION_CHUNKS_H +#define LLD_READER_WRITER_ELF_SECTION_CHUNKS_H + +#include "Chunk.h" +#include "Layout.h" +#include "TargetHandler.h" +#include "Writer.h" +#include "lld/Core/DefinedAtom.h" +#include "lld/Core/Parallel.h" +#include "lld/Core/range.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Dwarf.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileOutputBuffer.h" +#include <memory> +#include <mutex> + +namespace lld { +namespace elf { +template <class> class OutputSection; +using namespace llvm::ELF; +template <class ELFT> class Segment; + +/// \brief An ELF section. +template <class ELFT> class Section : public Chunk<ELFT> { +public: + Section(const ELFLinkingContext &context, StringRef sectionName, + StringRef chunkName, + typename Chunk<ELFT>::Kind k = Chunk<ELFT>::Kind::ELFSection) + : Chunk<ELFT>(chunkName, k, context), _outputSection(nullptr), _flags(0), + _entSize(0), _type(0), _link(0), _info(0), + _isFirstSectionInOutputSection(false), _segmentType(SHT_NULL), + _inputSectionName(sectionName), _outputSectionName(sectionName) {} + + /// \brief Modify the section contents before assigning virtual addresses + // or assigning file offsets + void doPreFlight() override {} + + /// \brief Finalize the section contents before writing + void finalize() override {} + + /// \brief Does this section have an output segment. + virtual bool hasOutputSegment() { + return false; + } + + /// Return if the section is a loadable section that occupies memory + virtual bool isLoadableSection() const { return false; } + + /// \brief Assign file offsets starting at offset. + virtual void assignFileOffsets(uint64_t offset) {} + + /// \brief Assign virtual addresses starting at addr. + virtual void assignVirtualAddress(uint64_t addr) {} + + uint64_t getFlags() const { return _flags; } + uint64_t getEntSize() const { return _entSize; } + uint32_t getType() const { return _type; } + uint32_t getLink() const { return _link; } + uint32_t getInfo() const { return _info; } + Layout::SegmentType getSegmentType() const { return _segmentType; } + + /// \brief Return the type of content that the section contains + virtual int getContentType() const override { + if (_flags & llvm::ELF::SHF_EXECINSTR) + return Chunk<ELFT>::ContentType::Code; + else if (_flags & llvm::ELF::SHF_WRITE) + return Chunk<ELFT>::ContentType::Data; + else if (_flags & llvm::ELF::SHF_ALLOC) + return Chunk<ELFT>::ContentType::Code; + else + return Chunk<ELFT>::ContentType::Unknown; + } + + /// \brief convert the segment type to a String for diagnostics and printing + /// purposes + StringRef segmentKindToStr() const; + + /// \brief Records the segmentType, that this section belongs to + void setSegmentType(const Layout::SegmentType segmentType) { + this->_segmentType = segmentType; + } + + virtual const AtomLayout *findAtomLayoutByName(StringRef) const { + return nullptr; + } + + void setOutputSection(OutputSection<ELFT> *os, bool isFirst = false) { + _outputSection = os; + _isFirstSectionInOutputSection = isFirst; + } + + static bool classof(const Chunk<ELFT> *c) { + return c->kind() == Chunk<ELFT>::Kind::ELFSection || + c->kind() == Chunk<ELFT>::Kind::AtomSection; + } + + uint64_t alignment() const override { + return _isFirstSectionInOutputSection ? _outputSection->alignment() + : this->_alignment; + } + + virtual StringRef inputSectionName() const { return _inputSectionName; } + + virtual StringRef outputSectionName() const { return _outputSectionName; } + + virtual void setOutputSectionName(StringRef outputSectionName) { + _outputSectionName = outputSectionName; + } + + void setArchiveNameOrPath(StringRef name) { _archivePath = name; } + + void setMemberNameOrPath(StringRef name) { _memberPath = name; } + + StringRef archivePath() { return _archivePath; } + + StringRef memberPath() { return _memberPath; } + +protected: + /// \brief OutputSection this Section is a member of, or nullptr. + OutputSection<ELFT> *_outputSection; + /// \brief ELF SHF_* flags. + uint64_t _flags; + /// \brief The size of each entity. + uint64_t _entSize; + /// \brief ELF SHT_* type. + uint32_t _type; + /// \brief sh_link field. + uint32_t _link; + /// \brief the sh_info field. + uint32_t _info; + /// \brief Is this the first section in the output section. + bool _isFirstSectionInOutputSection; + /// \brief the output ELF segment type of this section. + Layout::SegmentType _segmentType; + /// \brief Input section name. + StringRef _inputSectionName; + /// \brief Output section name. + StringRef _outputSectionName; + StringRef _archivePath; + StringRef _memberPath; +}; + +/// \brief A section containing atoms. +template <class ELFT> class AtomSection : public Section<ELFT> { +public: + AtomSection(const ELFLinkingContext &context, StringRef sectionName, + int32_t contentType, int32_t permissions, int32_t order) + : Section<ELFT>(context, sectionName, "AtomSection", + Chunk<ELFT>::Kind::AtomSection), + _contentType(contentType), _contentPermissions(permissions), + _isLoadedInMemory(true) { + this->setOrder(order); + + switch (contentType) { + case DefinedAtom::typeCode: + case DefinedAtom::typeDataFast: + case DefinedAtom::typeData: + case DefinedAtom::typeConstant: + case DefinedAtom::typeGOT: + case DefinedAtom::typeStub: + case DefinedAtom::typeResolver: + case DefinedAtom::typeThreadData: + this->_type = SHT_PROGBITS; + break; + + case DefinedAtom::typeThreadZeroFill: + case DefinedAtom::typeZeroFillFast: + case DefinedAtom::typeZeroFill: + this->_type = SHT_NOBITS; + break; + + case DefinedAtom::typeRONote: + case DefinedAtom::typeRWNote: + this->_type = SHT_NOTE; + break; + + case DefinedAtom::typeNoAlloc: + this->_type = SHT_PROGBITS; + this->_isLoadedInMemory = false; + break; + } + + switch (permissions) { + case DefinedAtom::permR__: + this->_flags = SHF_ALLOC; + break; + case DefinedAtom::permR_X: + this->_flags = SHF_ALLOC | SHF_EXECINSTR; + break; + case DefinedAtom::permRW_: + case DefinedAtom::permRW_L: + this->_flags = SHF_ALLOC | SHF_WRITE; + if (_contentType == DefinedAtom::typeThreadData || + _contentType == DefinedAtom::typeThreadZeroFill) + this->_flags |= SHF_TLS; + break; + case DefinedAtom::permRWX: + this->_flags = SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR; + break; + case DefinedAtom::perm___: + this->_flags = 0; + break; + } + } + + /// Align the offset to the required modulus defined by the atom alignment + uint64_t alignOffset(uint64_t offset, DefinedAtom::Alignment &atomAlign); + + /// Return if the section is a loadable section that occupies memory + bool isLoadableSection() const override { return _isLoadedInMemory; } + + // \brief Append an atom to a Section. The atom gets pushed into a vector + // contains the atom, the atom file offset, the atom virtual address + // the atom file offset is aligned appropriately as set by the Reader + virtual const lld::AtomLayout *appendAtom(const Atom *atom); + + /// \brief Set the virtual address of each Atom in the Section. This + /// routine gets called after the linker fixes up the virtual address + /// of the section + virtual void assignVirtualAddress(uint64_t addr) override { + parallel_for_each(_atoms.begin(), _atoms.end(), [&](AtomLayout *ai) { + ai->_virtualAddr = addr + ai->_fileOffset; + }); + } + + /// \brief Set the file offset of each Atom in the section. This routine + /// gets called after the linker fixes up the section offset + void assignFileOffsets(uint64_t offset) override { + parallel_for_each(_atoms.begin(), _atoms.end(), [&](AtomLayout *ai) { + ai->_fileOffset = offset + ai->_fileOffset; + }); + } + + /// \brief Find the Atom address given a name, this is needed to properly + /// apply relocation. The section class calls this to find the atom address + /// to fix the relocation + const AtomLayout *findAtomLayoutByName(StringRef name) const override { + for (auto ai : _atoms) + if (ai->_atom->name() == name) + return ai; + return nullptr; + } + + /// \brief Return the raw flags, we need this to sort segments + int64_t atomflags() const { return _contentPermissions; } + + /// Atom Iterators + typedef typename std::vector<lld::AtomLayout *>::iterator atom_iter; + + range<atom_iter> atoms() { return _atoms; } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) override; + + static bool classof(const Chunk<ELFT> *c) { + return c->kind() == Chunk<ELFT>::Kind::AtomSection; + } + +protected: + llvm::BumpPtrAllocator _alloc; + int32_t _contentType; + int32_t _contentPermissions; + bool _isLoadedInMemory; + std::vector<lld::AtomLayout *> _atoms; + mutable std::mutex _outputMutex; + + void printError(const std::string &errorStr, const AtomLayout &atom, + const Reference &ref) const { + StringRef kindValStr; + if (!this->_context.registry().referenceKindToString(ref.kindNamespace(), + ref.kindArch(), + ref.kindValue(), + kindValStr)) { + kindValStr = "unknown"; + } + + std::string errStr = (Twine(errorStr) + " in file " + + atom._atom->file().path() + + ": reference from " + atom._atom->name() + + "+" + Twine(ref.offsetInAtom()) + + " to " + ref.target()->name() + + "+" + Twine(ref.addend()) + + " of type " + Twine(ref.kindValue()) + + " (" + kindValStr + ")\n").str(); + + // Take the lock to prevent output getting interleaved between threads + std::lock_guard<std::mutex> lock(_outputMutex); + llvm::errs() << errStr; + } +}; + +/// Align the offset to the required modulus defined by the atom alignment +template <class ELFT> +uint64_t AtomSection<ELFT>::alignOffset(uint64_t offset, + DefinedAtom::Alignment &atomAlign) { + uint64_t requiredModulus = atomAlign.modulus; + uint64_t alignment = 1u << atomAlign.powerOf2; + uint64_t currentModulus = (offset % alignment); + uint64_t retOffset = offset; + if (currentModulus != requiredModulus) { + if (requiredModulus > currentModulus) + retOffset += requiredModulus - currentModulus; + else + retOffset += alignment + requiredModulus - currentModulus; + } + return retOffset; +} + +// \brief Append an atom to a Section. The atom gets pushed into a vector +// contains the atom, the atom file offset, the atom virtual address +// the atom file offset is aligned appropriately as set by the Reader +template <class ELFT> +const lld::AtomLayout *AtomSection<ELFT>::appendAtom(const Atom *atom) { + const DefinedAtom *definedAtom = cast<DefinedAtom>(atom); + + DefinedAtom::Alignment atomAlign = definedAtom->alignment(); + uint64_t alignment = 1u << atomAlign.powerOf2; + // Align the atom to the required modulus/ align the file offset and the + // memory offset separately this is required so that BSS symbols are handled + // properly as the BSS symbols only occupy memory size and not file size + uint64_t fOffset = alignOffset(this->fileSize(), atomAlign); + uint64_t mOffset = alignOffset(this->memSize(), atomAlign); + switch(definedAtom->contentType()) { + case DefinedAtom::typeCode: + case DefinedAtom::typeConstant: + case DefinedAtom::typeData: + case DefinedAtom::typeDataFast: + case DefinedAtom::typeZeroFillFast: + case DefinedAtom::typeGOT: + case DefinedAtom::typeStub: + case DefinedAtom::typeResolver: + case DefinedAtom::typeThreadData: + case DefinedAtom::typeRONote: + case DefinedAtom::typeRWNote: + _atoms.push_back(new (_alloc) lld::AtomLayout(atom, fOffset, 0)); + this->_fsize = fOffset + definedAtom->size(); + this->_msize = mOffset + definedAtom->size(); + DEBUG_WITH_TYPE("Section", + llvm::dbgs() << "[" << this->name() << " " << this << "] " + << "Adding atom: " << atom->name() << "@" + << fOffset << "\n"); + break; + case DefinedAtom::typeNoAlloc: + _atoms.push_back(new (_alloc) lld::AtomLayout(atom, fOffset, 0)); + this->_fsize = fOffset + definedAtom->size(); + DEBUG_WITH_TYPE("Section", llvm::dbgs() << "[" << this->name() << " " + << this << "] " + << "Adding atom: " << atom->name() + << "@" << fOffset << "\n"); + break; + case DefinedAtom::typeThreadZeroFill: + case DefinedAtom::typeZeroFill: + _atoms.push_back(new (_alloc) lld::AtomLayout(atom, mOffset, 0)); + this->_msize = mOffset + definedAtom->size(); + break; + default: + llvm::dbgs() << definedAtom->contentType() << "\n"; + llvm_unreachable("Uexpected content type."); + } + // Set the section alignment to the largest alignment + // std::max doesn't support uint64_t + if (this->_alignment < alignment) + this->_alignment = alignment; + + if (_atoms.size()) + return _atoms.back(); + return nullptr; +} + +/// \brief convert the segment type to a String for diagnostics +/// and printing purposes +template <class ELFT> StringRef Section<ELFT>::segmentKindToStr() const { + switch(_segmentType) { + case llvm::ELF::PT_DYNAMIC: + return "DYNAMIC"; + case llvm::ELF::PT_INTERP: + return "INTERP"; + case llvm::ELF::PT_LOAD: + return "LOAD"; + case llvm::ELF::PT_GNU_EH_FRAME: + return "EH_FRAME"; + case llvm::ELF::PT_GNU_RELRO: + return "GNU_RELRO"; + case llvm::ELF::PT_NOTE: + return "NOTE"; + case llvm::ELF::PT_NULL: + return "NULL"; + case llvm::ELF::PT_TLS: + return "TLS"; + default: + return "UNKNOWN"; + } +} + +/// \brief Write the section and the atom contents to the buffer +template <class ELFT> +void AtomSection<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + bool success = true; + parallel_for_each(_atoms.begin(), _atoms.end(), [&](lld::AtomLayout * ai) { + DEBUG_WITH_TYPE("Section", + llvm::dbgs() << "Writing atom: " << ai->_atom->name() + << " | " << ai->_fileOffset << "\n"); + const DefinedAtom *definedAtom = cast<DefinedAtom>(ai->_atom); + if (!definedAtom->occupiesDiskSpace()) + return; + // Copy raw content of atom to file buffer. + ArrayRef<uint8_t> content = definedAtom->rawContent(); + uint64_t contentSize = content.size(); + if (contentSize == 0) + return; + uint8_t *atomContent = chunkBuffer + ai->_fileOffset; + std::memcpy(atomContent, content.data(), contentSize); + const TargetRelocationHandler &relHandler = + this->_context.template getTargetHandler<ELFT>().getRelocationHandler(); + for (const auto ref : *definedAtom) { + if (std::error_code ec = relHandler.applyRelocation(*writer, buffer, + *ai, *ref)) { + printError(ec.message(), *ai, *ref); + success = false; + } + } + }); + if (!success) + llvm::report_fatal_error("relocating output"); +} + +/// \brief A OutputSection represents a set of sections grouped by the same +/// name. The output file that gets written by the linker has sections grouped +/// by similar names +template <class ELFT> class OutputSection { +public: + // Iterators + typedef typename std::vector<Chunk<ELFT> *>::iterator ChunkIter; + + OutputSection(StringRef name); + + // Appends a section into the list of sections that are part of this Output + // Section + void appendSection(Chunk<ELFT> *c); + + // Set the OutputSection is associated with a segment + void setHasSegment() { _hasSegment = true; } + + /// Sets the ordinal + void setOrdinal(uint64_t ordinal) { _ordinal = ordinal; } + + /// Sets the Memory size + void setMemSize(uint64_t memsz) { _memSize = memsz; } + + /// Sets the size fo the output Section. + void setSize(uint64_t fsiz) { _size = fsiz; } + + // The offset of the first section contained in the output section is + // contained here. + void setFileOffset(uint64_t foffset) { _fileOffset = foffset; } + + // Sets the starting address of the section + void setAddr(uint64_t addr) { _virtualAddr = addr; } + + // Is the section loadable? + bool isLoadableSection() const { return _isLoadableSection; } + + // Set section Loadable + void setLoadableSection(bool isLoadable) { + _isLoadableSection = isLoadable; + } + + void setLink(uint64_t link) { _link = link; } + + void setInfo(uint64_t info) { _shInfo = info; } + + void setFlag(uint64_t flags) { _flags = flags; } + + void setType(int16_t type) { _type = type; } + + range<ChunkIter> sections() { return _sections; } + + // The below functions returns the properties of the OutputSection. + bool hasSegment() const { return _hasSegment; } + + StringRef name() const { return _name; } + + int64_t shinfo() const { return _shInfo; } + + uint64_t alignment() const { return _alignment; } + + int64_t link() const { return _link; } + + int64_t type() const { return _type; } + + uint64_t virtualAddr() const { return _virtualAddr; } + + int64_t ordinal() const { return _ordinal; } + + int64_t kind() const { return _kind; } + + uint64_t fileSize() const { return _size; } + + int64_t entsize() const { return _entSize; } + + uint64_t fileOffset() const { return _fileOffset; } + + int64_t flags() const { return _flags; } + + uint64_t memSize() { return _memSize; } + +private: + StringRef _name; + bool _hasSegment; + uint64_t _ordinal; + uint64_t _flags; + uint64_t _size; + uint64_t _memSize; + uint64_t _fileOffset; + uint64_t _virtualAddr; + int64_t _shInfo; + int64_t _entSize; + int64_t _link; + uint64_t _alignment; + int64_t _kind; + int64_t _type; + bool _isLoadableSection; + std::vector<Chunk<ELFT> *> _sections; +}; + +/// OutputSection +template <class ELFT> +OutputSection<ELFT>::OutputSection(StringRef name) + : _name(name), _hasSegment(false), _ordinal(0), _flags(0), _size(0), + _memSize(0), _fileOffset(0), _virtualAddr(0), _shInfo(0), _entSize(0), + _link(0), _alignment(0), _kind(0), _type(0), _isLoadableSection(false) {} + +template <class ELFT> void OutputSection<ELFT>::appendSection(Chunk<ELFT> *c) { + if (c->alignment() > _alignment) + _alignment = c->alignment(); + if (const auto section = dyn_cast<Section<ELFT>>(c)) { + assert(!_link && "Section already has a link!"); + _link = section->getLink(); + _shInfo = section->getInfo(); + _entSize = section->getEntSize(); + _type = section->getType(); + if (_flags < section->getFlags()) + _flags = section->getFlags(); + section->setOutputSection(this, (_sections.size() == 0)); + } + _kind = c->kind(); + _sections.push_back(c); +} + +/// \brief The class represents the ELF String Table +template<class ELFT> +class StringTable : public Section<ELFT> { +public: + StringTable(const ELFLinkingContext &, const char *str, int32_t order, + bool dynamic = false); + + uint64_t addString(StringRef symname); + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) override; + + void setNumEntries(int64_t numEntries) { _stringMap.resize(numEntries); } + +private: + std::vector<StringRef> _strings; + + 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 typename llvm::DenseMap<StringRef, uint64_t, + StringRefMappingInfo> StringMapT; + typedef typename StringMapT::iterator StringMapTIter; + StringMapT _stringMap; +}; + +template <class ELFT> +StringTable<ELFT>::StringTable(const ELFLinkingContext &context, + const char *str, int32_t order, bool dynamic) + : Section<ELFT>(context, str, "StringTable") { + // the string table has a NULL entry for which + // add an empty string + _strings.push_back(""); + this->_fsize = 1; + this->_alignment = 1; + this->setOrder(order); + this->_type = SHT_STRTAB; + if (dynamic) { + this->_flags = SHF_ALLOC; + this->_msize = this->_fsize; + } +} + +template <class ELFT> uint64_t StringTable<ELFT>::addString(StringRef symname) { + if (symname.empty()) + return 0; + StringMapTIter stringIter = _stringMap.find(symname); + if (stringIter == _stringMap.end()) { + _strings.push_back(symname); + uint64_t offset = this->_fsize; + this->_fsize += symname.size() + 1; + if (this->_flags & SHF_ALLOC) + this->_msize = this->_fsize; + _stringMap[symname] = offset; + return offset; + } + return stringIter->second; +} + +template <class ELFT> +void StringTable<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + for (auto si : _strings) { + memcpy(dest, si.data(), si.size()); + dest += si.size(); + memcpy(dest, "", 1); + dest += 1; + } +} + +/// \brief The SymbolTable class represents the symbol table in a ELF file +template<class ELFT> +class SymbolTable : public Section<ELFT> { + typedef typename llvm::object::ELFDataTypeTypedefHelper<ELFT>::Elf_Addr + Elf_Addr; + +public: + typedef llvm::object::Elf_Sym_Impl<ELFT> Elf_Sym; + + SymbolTable(const ELFLinkingContext &context, const char *str, int32_t order); + + /// \brief set the number of entries that would exist in the symbol + /// table for the current link + void setNumEntries(int64_t numEntries) const { + if (_stringSection) + _stringSection->setNumEntries(numEntries); + } + + /// \brief return number of entries + std::size_t size() const { return _symbolTable.size(); } + + void addSymbol(const Atom *atom, int32_t sectionIndex, uint64_t addr = 0, + const lld::AtomLayout *layout = nullptr); + + /// \brief Get the symbol table index for an Atom. If it's not in the symbol + /// table, return STN_UNDEF. + uint32_t getSymbolTableIndex(const Atom *a) const { + for (size_t i = 0, e = _symbolTable.size(); i < e; ++i) + if (_symbolTable[i]._atom == a) + return i; + return STN_UNDEF; + } + + void finalize() override { finalize(true); } + + virtual void sortSymbols() { + std::stable_sort(_symbolTable.begin(), _symbolTable.end(), + [](const SymbolEntry & A, const SymbolEntry & B) { + return A._symbol.getBinding() < B._symbol.getBinding(); + }); + } + + virtual void addAbsoluteAtom(Elf_Sym &sym, const AbsoluteAtom *aa, + int64_t addr); + + virtual void addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, + int64_t addr); + + virtual void addUndefinedAtom(Elf_Sym &sym, const UndefinedAtom *ua); + + virtual void addSharedLibAtom(Elf_Sym &sym, const SharedLibraryAtom *sla); + + virtual void finalize(bool sort); + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) override; + + void setStringSection(StringTable<ELFT> *s) { _stringSection = s; } + + StringTable<ELFT> *getStringTable() const { return _stringSection; } + +protected: + struct SymbolEntry { + SymbolEntry(const Atom *a, const Elf_Sym &sym, + const lld::AtomLayout *layout) + : _atom(a), _atomLayout(layout), _symbol(sym) {} + + const Atom *_atom; + const lld::AtomLayout *_atomLayout; + Elf_Sym _symbol; + }; + + llvm::BumpPtrAllocator _symbolAllocate; + StringTable<ELFT> *_stringSection; + std::vector<SymbolEntry> _symbolTable; +}; + +/// ELF Symbol Table +template <class ELFT> +SymbolTable<ELFT>::SymbolTable(const ELFLinkingContext &context, + const char *str, int32_t order) + : Section<ELFT>(context, str, "SymbolTable") { + this->setOrder(order); + Elf_Sym symbol; + std::memset(&symbol, 0, sizeof(Elf_Sym)); + _symbolTable.push_back(SymbolEntry(nullptr, symbol, nullptr)); + this->_entSize = sizeof(Elf_Sym); + this->_fsize = sizeof(Elf_Sym); + this->_alignment = sizeof(Elf_Addr); + this->_type = SHT_SYMTAB; +} + +template <class ELFT> +void SymbolTable<ELFT>::addDefinedAtom(Elf_Sym &sym, const DefinedAtom *da, + int64_t addr) { + unsigned char binding = 0, type = 0; + sym.st_size = da->size(); + DefinedAtom::ContentType ct; + switch (ct = da->contentType()) { + case DefinedAtom::typeCode: + case DefinedAtom::typeStub: + sym.st_value = addr; + type = llvm::ELF::STT_FUNC; + break; + case DefinedAtom::typeResolver: + sym.st_value = addr; + type = llvm::ELF::STT_GNU_IFUNC; + break; + case DefinedAtom::typeDataFast: + case DefinedAtom::typeData: + case DefinedAtom::typeConstant: + sym.st_value = addr; + type = llvm::ELF::STT_OBJECT; + break; + case DefinedAtom::typeGOT: + sym.st_value = addr; + type = llvm::ELF::STT_NOTYPE; + break; + case DefinedAtom::typeZeroFill: + case DefinedAtom::typeZeroFillFast: + type = llvm::ELF::STT_OBJECT; + sym.st_value = addr; + break; + case DefinedAtom::typeThreadData: + case DefinedAtom::typeThreadZeroFill: + type = llvm::ELF::STT_TLS; + sym.st_value = addr; + break; + default: + type = llvm::ELF::STT_NOTYPE; + } + if (da->customSectionName() == da->name()) + type = llvm::ELF::STT_SECTION; + + if (da->scope() == DefinedAtom::scopeTranslationUnit) + binding = llvm::ELF::STB_LOCAL; + else + binding = llvm::ELF::STB_GLOBAL; + + sym.setBindingAndType(binding, type); +} + +template <class ELFT> +void SymbolTable<ELFT>::addAbsoluteAtom(Elf_Sym &sym, const AbsoluteAtom *aa, + int64_t addr) { + unsigned char binding = 0, type = 0; + type = llvm::ELF::STT_OBJECT; + sym.st_shndx = llvm::ELF::SHN_ABS; + switch (aa->scope()) { + case AbsoluteAtom::scopeLinkageUnit: + sym.setVisibility(llvm::ELF::STV_HIDDEN); + binding = llvm::ELF::STB_LOCAL; + break; + case AbsoluteAtom::scopeTranslationUnit: + binding = llvm::ELF::STB_LOCAL; + break; + case AbsoluteAtom::scopeGlobal: + binding = llvm::ELF::STB_GLOBAL; + break; + } + sym.st_value = addr; + sym.setBindingAndType(binding, type); +} + +template <class ELFT> +void SymbolTable<ELFT>::addSharedLibAtom(Elf_Sym &sym, + const SharedLibraryAtom *aa) { + unsigned char binding = 0, type = 0; + if (aa->type() == SharedLibraryAtom::Type::Data) { + type = llvm::ELF::STT_OBJECT; + sym.st_size = aa->size(); + } else + type = llvm::ELF::STT_FUNC; + sym.st_shndx = llvm::ELF::SHN_UNDEF; + binding = llvm::ELF::STB_GLOBAL; + sym.setBindingAndType(binding, type); +} + +template <class ELFT> +void SymbolTable<ELFT>::addUndefinedAtom(Elf_Sym &sym, + const UndefinedAtom *ua) { + unsigned char binding = 0, type = 0; + sym.st_value = 0; + type = llvm::ELF::STT_NOTYPE; + if (ua->canBeNull()) + binding = llvm::ELF::STB_WEAK; + else + binding = llvm::ELF::STB_GLOBAL; + sym.setBindingAndType(binding, type); +} + +/// Add a symbol to the symbol Table, definedAtoms which get added to the symbol +/// section don't have their virtual addresses set at the time of adding the +/// symbol to the symbol table(Example: dynamic symbols), the addresses needs +/// to be updated in the table before writing the dynamic symbol table +/// information +template <class ELFT> +void SymbolTable<ELFT>::addSymbol(const Atom *atom, int32_t sectionIndex, + uint64_t addr, + const lld::AtomLayout *atomLayout) { + Elf_Sym symbol; + + if (atom->name().empty()) + return; + + symbol.st_name = _stringSection->addString(atom->name()); + symbol.st_size = 0; + symbol.st_shndx = sectionIndex; + symbol.st_value = 0; + symbol.st_other = 0; + symbol.setVisibility(llvm::ELF::STV_DEFAULT); + + // Add all the atoms + if (const DefinedAtom *da = dyn_cast<const DefinedAtom>(atom)) + addDefinedAtom(symbol, da, addr); + else if (const AbsoluteAtom *aa = dyn_cast<const AbsoluteAtom>(atom)) + addAbsoluteAtom(symbol, aa, addr); + else if (isa<const SharedLibraryAtom>(atom)) + addSharedLibAtom(symbol, dyn_cast<SharedLibraryAtom>(atom)); + else + addUndefinedAtom(symbol, dyn_cast<UndefinedAtom>(atom)); + + _symbolTable.push_back(SymbolEntry(atom, symbol, atomLayout)); + this->_fsize += sizeof(Elf_Sym); + if (this->_flags & SHF_ALLOC) + this->_msize = this->_fsize; +} + +template <class ELFT> void SymbolTable<ELFT>::finalize(bool sort) { + // sh_info should be one greater than last symbol with STB_LOCAL binding + // we sort the symbol table to keep all local symbols at the beginning + if (sort) + sortSymbols(); + + uint16_t shInfo = 0; + for (const auto &i : _symbolTable) { + if (i._symbol.getBinding() != llvm::ELF::STB_LOCAL) + break; + shInfo++; + } + this->_info = shInfo; + this->_link = _stringSection->ordinal(); + if (this->_outputSection) { + this->_outputSection->setInfo(this->_info); + this->_outputSection->setLink(this->_link); + } +} + +template <class ELFT> +void SymbolTable<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + for (const auto &sti : _symbolTable) { + memcpy(dest, &sti._symbol, sizeof(Elf_Sym)); + dest += sizeof(Elf_Sym); + } +} + +template <class ELFT> class HashSection; + +template <class ELFT> class DynamicSymbolTable : public SymbolTable<ELFT> { +public: + DynamicSymbolTable(const ELFLinkingContext &context, + TargetLayout<ELFT> &layout, const char *str, int32_t order) + : SymbolTable<ELFT>(context, str, order), _hashTable(nullptr), + _layout(layout) { + this->_type = SHT_DYNSYM; + this->_flags = SHF_ALLOC; + this->_msize = this->_fsize; + } + + // Set the dynamic hash table for symbols to be added into + void setHashTable(HashSection<ELFT> *hashTable) { _hashTable = hashTable; } + + // Add all the dynamic symbos to the hash table + void addSymbolsToHashTable() { + int index = 0; + for (auto &ste : this->_symbolTable) { + if (!ste._atom) + _hashTable->addSymbol("", index); + else + _hashTable->addSymbol(ste._atom->name(), index); + ++index; + } + } + + void finalize() override { + // Defined symbols which have been added into the dynamic symbol table + // don't have their addresses known until addresses have been assigned + // so let's update the symbol values after they have got assigned + for (auto &ste: this->_symbolTable) { + const lld::AtomLayout *atomLayout = ste._atomLayout; + if (!atomLayout) + continue; + ste._symbol.st_value = atomLayout->_virtualAddr; + } + + // Don't sort the symbols + SymbolTable<ELFT>::finalize(false); + } + +protected: + HashSection<ELFT> *_hashTable; + TargetLayout<ELFT> &_layout; +}; + +template <class ELFT> class RelocationTable : public Section<ELFT> { +public: + typedef llvm::object::Elf_Rel_Impl<ELFT, false> Elf_Rel; + typedef llvm::object::Elf_Rel_Impl<ELFT, true> Elf_Rela; + + RelocationTable(const ELFLinkingContext &context, StringRef str, + int32_t order) + : Section<ELFT>(context, str, "RelocationTable"), _symbolTable(nullptr) { + this->setOrder(order); + this->_flags = SHF_ALLOC; + // Set the alignment properly depending on the target architecture + this->_alignment = ELFT::Is64Bits ? 8 : 4; + if (context.isRelaOutputFormat()) { + this->_entSize = sizeof(Elf_Rela); + this->_type = SHT_RELA; + } else { + this->_entSize = sizeof(Elf_Rel); + this->_type = SHT_REL; + } + } + + /// \returns the index of the relocation added. + uint32_t addRelocation(const DefinedAtom &da, const Reference &r) { + _relocs.emplace_back(&da, &r); + this->_fsize = _relocs.size() * this->_entSize; + this->_msize = this->_fsize; + return _relocs.size() - 1; + } + + bool getRelocationIndex(const Reference &r, uint32_t &res) { + auto rel = std::find_if( + _relocs.begin(), _relocs.end(), + [&](const std::pair<const DefinedAtom *, const Reference *> &p) { + if (p.second == &r) + return true; + return false; + }); + if (rel == _relocs.end()) + return false; + res = std::distance(_relocs.begin(), rel); + return true; + } + + void setSymbolTable(const DynamicSymbolTable<ELFT> *symbolTable) { + _symbolTable = symbolTable; + } + + /// \brief Check if any relocation modifies a read-only section. + bool canModifyReadonlySection() const { + for (const auto &rel : _relocs) { + const DefinedAtom *atom = rel.first; + if ((atom->permissions() & DefinedAtom::permRW_) != DefinedAtom::permRW_) + return true; + } + return false; + } + + void finalize() override { + this->_link = _symbolTable ? _symbolTable->ordinal() : 0; + if (this->_outputSection) + this->_outputSection->setLink(this->_link); + } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) override { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + for (const auto &rel : _relocs) { + if (this->_context.isRelaOutputFormat()) { + auto &r = *reinterpret_cast<Elf_Rela *>(dest); + writeRela(writer, r, *rel.first, *rel.second); + DEBUG_WITH_TYPE("ELFRelocationTable", + llvm::dbgs() + << rel.second->kindValue() << " relocation at " + << rel.first->name() << "@" << r.r_offset << " to " + << rel.second->target()->name() << "@" << r.r_addend + << "\n";); + } else { + auto &r = *reinterpret_cast<Elf_Rel *>(dest); + writeRel(writer, r, *rel.first, *rel.second); + DEBUG_WITH_TYPE("ELFRelocationTable", + llvm::dbgs() << rel.second->kindValue() + << " relocation at " << rel.first->name() + << "@" << r.r_offset << " to " + << rel.second->target()->name() << "\n";); + } + dest += this->_entSize; + } + } + +protected: + const DynamicSymbolTable<ELFT> *_symbolTable; + + virtual void writeRela(ELFWriter *writer, Elf_Rela &r, + const DefinedAtom &atom, const Reference &ref) { + r.setSymbolAndType(getSymbolIndex(ref.target()), ref.kindValue(), false); + r.r_offset = writer->addressOfAtom(&atom) + ref.offsetInAtom(); + // The addend is used only by relative relocations + if (this->_context.isRelativeReloc(ref)) + r.r_addend = writer->addressOfAtom(ref.target()) + ref.addend(); + else + r.r_addend = 0; + } + + virtual void writeRel(ELFWriter *writer, Elf_Rel &r, const DefinedAtom &atom, + const Reference &ref) { + r.setSymbolAndType(getSymbolIndex(ref.target()), ref.kindValue(), false); + r.r_offset = writer->addressOfAtom(&atom) + ref.offsetInAtom(); + } + + uint32_t getSymbolIndex(const Atom *a) { + return _symbolTable ? _symbolTable->getSymbolTableIndex(a) + : (uint32_t)STN_UNDEF; + } + +private: + std::vector<std::pair<const DefinedAtom *, const Reference *> > _relocs; +}; + +template <class ELFT> class HashSection; + +template <class ELFT> class DynamicTable : public Section<ELFT> { +public: + typedef llvm::object::Elf_Dyn_Impl<ELFT> Elf_Dyn; + typedef std::vector<Elf_Dyn> EntriesT; + + DynamicTable(const ELFLinkingContext &context, TargetLayout<ELFT> &layout, + StringRef str, int32_t order) + : Section<ELFT>(context, str, "DynamicSection"), _layout(layout) { + this->setOrder(order); + this->_entSize = sizeof(Elf_Dyn); + this->_alignment = ELFT::Is64Bits ? 8 : 4; + // Reserve space for the DT_NULL entry. + this->_fsize = sizeof(Elf_Dyn); + this->_msize = sizeof(Elf_Dyn); + this->_type = SHT_DYNAMIC; + this->_flags = SHF_ALLOC; + } + + range<typename EntriesT::iterator> entries() { return _entries; } + + /// \returns the index of the entry. + std::size_t addEntry(Elf_Dyn e) { + _entries.push_back(e); + this->_fsize = (_entries.size() * sizeof(Elf_Dyn)) + sizeof(Elf_Dyn); + this->_msize = this->_fsize; + return _entries.size() - 1; + } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) override { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + // Add the null entry. + Elf_Dyn d; + d.d_tag = 0; + d.d_un.d_val = 0; + _entries.push_back(d); + std::memcpy(dest, _entries.data(), this->_fsize); + } + + virtual void createDefaultEntries() { + bool isRela = this->_context.isRelaOutputFormat(); + + Elf_Dyn dyn; + dyn.d_un.d_val = 0; + + dyn.d_tag = DT_HASH; + _dt_hash = addEntry(dyn); + dyn.d_tag = DT_STRTAB; + _dt_strtab = addEntry(dyn); + dyn.d_tag = DT_SYMTAB; + _dt_symtab = addEntry(dyn); + dyn.d_tag = DT_STRSZ; + _dt_strsz = addEntry(dyn); + dyn.d_tag = DT_SYMENT; + _dt_syment = addEntry(dyn); + if (_layout.hasDynamicRelocationTable()) { + dyn.d_tag = isRela ? DT_RELA : DT_REL; + _dt_rela = addEntry(dyn); + dyn.d_tag = isRela ? DT_RELASZ : DT_RELSZ; + _dt_relasz = addEntry(dyn); + dyn.d_tag = isRela ? DT_RELAENT : DT_RELENT; + _dt_relaent = addEntry(dyn); + + if (_layout.getDynamicRelocationTable()->canModifyReadonlySection()) { + dyn.d_tag = DT_TEXTREL; + _dt_textrel = addEntry(dyn); + } + } + if (_layout.hasPLTRelocationTable()) { + dyn.d_tag = DT_PLTRELSZ; + _dt_pltrelsz = addEntry(dyn); + dyn.d_tag = getGotPltTag(); + _dt_pltgot = addEntry(dyn); + dyn.d_tag = DT_PLTREL; + dyn.d_un.d_val = isRela ? DT_RELA : DT_REL; + _dt_pltrel = addEntry(dyn); + dyn.d_un.d_val = 0; + dyn.d_tag = DT_JMPREL; + _dt_jmprel = addEntry(dyn); + } + } + + void doPreFlight() override { + Elf_Dyn dyn; + dyn.d_un.d_val = 0; + auto initArray = _layout.findOutputSection(".init_array"); + auto finiArray = _layout.findOutputSection(".fini_array"); + if (initArray) { + dyn.d_tag = DT_INIT_ARRAY; + _dt_init_array = addEntry(dyn); + dyn.d_tag = DT_INIT_ARRAYSZ; + _dt_init_arraysz = addEntry(dyn); + } + if (finiArray) { + dyn.d_tag = DT_FINI_ARRAY; + _dt_fini_array = addEntry(dyn); + dyn.d_tag = DT_FINI_ARRAYSZ; + _dt_fini_arraysz = addEntry(dyn); + } + if (getInitAtomLayout()) { + dyn.d_tag = DT_INIT; + _dt_init = addEntry(dyn); + } + if (getFiniAtomLayout()) { + dyn.d_tag = DT_FINI; + _dt_fini = addEntry(dyn); + } + } + + /// \brief Dynamic table tag for .got.plt section referencing. + /// Usually but not always targets use DT_PLTGOT for that. + virtual int64_t getGotPltTag() { return DT_PLTGOT; } + + void finalize() override { + StringTable<ELFT> *dynamicStringTable = + _dynamicSymbolTable->getStringTable(); + this->_link = dynamicStringTable->ordinal(); + if (this->_outputSection) { + this->_outputSection->setType(this->_type); + this->_outputSection->setInfo(this->_info); + this->_outputSection->setLink(this->_link); + } + } + + void setSymbolTable(DynamicSymbolTable<ELFT> *dynsym) { + _dynamicSymbolTable = dynsym; + } + + const DynamicSymbolTable<ELFT> *getSymbolTable() const { + return _dynamicSymbolTable; + } + + void setHashTable(HashSection<ELFT> *hsh) { _hashTable = hsh; } + + virtual void updateDynamicTable() { + StringTable<ELFT> *dynamicStringTable = + _dynamicSymbolTable->getStringTable(); + _entries[_dt_hash].d_un.d_val = _hashTable->virtualAddr(); + _entries[_dt_strtab].d_un.d_val = dynamicStringTable->virtualAddr(); + _entries[_dt_symtab].d_un.d_val = _dynamicSymbolTable->virtualAddr(); + _entries[_dt_strsz].d_un.d_val = dynamicStringTable->memSize(); + _entries[_dt_syment].d_un.d_val = _dynamicSymbolTable->getEntSize(); + auto initArray = _layout.findOutputSection(".init_array"); + if (initArray) { + _entries[_dt_init_array].d_un.d_val = initArray->virtualAddr(); + _entries[_dt_init_arraysz].d_un.d_val = initArray->memSize(); + } + auto finiArray = _layout.findOutputSection(".fini_array"); + if (finiArray) { + _entries[_dt_fini_array].d_un.d_val = finiArray->virtualAddr(); + _entries[_dt_fini_arraysz].d_un.d_val = finiArray->memSize(); + } + if (const auto *al = getInitAtomLayout()) + _entries[_dt_init].d_un.d_val = getAtomVirtualAddress(al); + if (const auto *al = getFiniAtomLayout()) + _entries[_dt_fini].d_un.d_val = getAtomVirtualAddress(al); + if (_layout.hasDynamicRelocationTable()) { + auto relaTbl = _layout.getDynamicRelocationTable(); + _entries[_dt_rela].d_un.d_val = relaTbl->virtualAddr(); + _entries[_dt_relasz].d_un.d_val = relaTbl->memSize(); + _entries[_dt_relaent].d_un.d_val = relaTbl->getEntSize(); + } + if (_layout.hasPLTRelocationTable()) { + auto relaTbl = _layout.getPLTRelocationTable(); + _entries[_dt_jmprel].d_un.d_val = relaTbl->virtualAddr(); + _entries[_dt_pltrelsz].d_un.d_val = relaTbl->memSize(); + auto gotplt = _layout.findOutputSection(".got.plt"); + _entries[_dt_pltgot].d_un.d_val = gotplt->virtualAddr(); + } + } + +protected: + EntriesT _entries; + + /// \brief Return a virtual address (maybe adjusted) for the atom layout + /// Some targets like microMIPS and ARM Thumb use the last bit + /// of a symbol's value to mark 'compressed' code. This function allows + /// to adjust a virtal address before using it in the dynamic table tag. + virtual uint64_t getAtomVirtualAddress(const AtomLayout *al) const { + return al->_virtualAddr; + } + +private: + std::size_t _dt_hash; + std::size_t _dt_strtab; + std::size_t _dt_symtab; + std::size_t _dt_rela; + std::size_t _dt_relasz; + std::size_t _dt_relaent; + std::size_t _dt_strsz; + std::size_t _dt_syment; + std::size_t _dt_pltrelsz; + std::size_t _dt_pltgot; + std::size_t _dt_pltrel; + std::size_t _dt_jmprel; + std::size_t _dt_init_array; + std::size_t _dt_init_arraysz; + std::size_t _dt_fini_array; + std::size_t _dt_fini_arraysz; + std::size_t _dt_textrel; + std::size_t _dt_init; + std::size_t _dt_fini; + TargetLayout<ELFT> &_layout; + DynamicSymbolTable<ELFT> *_dynamicSymbolTable; + HashSection<ELFT> *_hashTable; + + const AtomLayout *getInitAtomLayout() { + auto al = _layout.findAtomLayoutByName(this->_context.initFunction()); + if (al && isa<DefinedAtom>(al->_atom)) + return al; + return nullptr; + } + + const AtomLayout *getFiniAtomLayout() { + auto al = _layout.findAtomLayoutByName(this->_context.finiFunction()); + if (al && isa<DefinedAtom>(al->_atom)) + return al; + return nullptr; + } +}; + +template <class ELFT> class InterpSection : public Section<ELFT> { +public: + InterpSection(const ELFLinkingContext &context, StringRef str, int32_t order, + StringRef interp) + : Section<ELFT>(context, str, "Dynamic:Interp"), _interp(interp) { + this->setOrder(order); + this->_alignment = 1; + // + 1 for null term. + this->_fsize = interp.size() + 1; + this->_msize = this->_fsize; + this->_type = SHT_PROGBITS; + this->_flags = SHF_ALLOC; + } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + std::memcpy(dest, _interp.data(), _interp.size()); + } + +private: + StringRef _interp; +}; + +/// The hash table in the dynamic linker is organized into +/// +/// [ nbuckets ] +/// [ nchains ] +/// [ buckets[0] ] +/// ......................... +/// [ buckets[nbuckets-1] ] +/// [ chains[0] ] +/// ......................... +/// [ chains[nchains - 1] ] +/// +/// nbuckets - total number of hash buckets +/// nchains is equal to the number of dynamic symbols. +/// +/// The symbol is searched by the dynamic linker using the below approach. +/// * Calculate the hash of the symbol that needs to be searched +/// * Take the value from the buckets[hash % nbuckets] as the index of symbol +/// * Compare the symbol's name, if true return, if false, look through the +/// * array since there was a collision + +template <class ELFT> class HashSection : public Section<ELFT> { + struct SymbolTableEntry { + StringRef _name; + uint32_t _index; + }; + +public: + HashSection(const ELFLinkingContext &context, StringRef name, int32_t order) + : Section<ELFT>(context, name, "Dynamic:Hash"), _symbolTable(nullptr) { + this->setOrder(order); + this->_entSize = 4; + this->_type = SHT_HASH; + this->_flags = SHF_ALLOC; + this->_alignment = ELFT::Is64Bits ? 8 : 4; + this->_fsize = 0; + this->_msize = 0; + } + + /// \brief add the dynamic symbol into the table so that the + /// hash could be calculated + void addSymbol(StringRef name, uint32_t index) { + SymbolTableEntry ste; + ste._name = name; + ste._index = index; + _entries.push_back(ste); + } + + /// \brief Set the dynamic symbol table + void setSymbolTable(const DynamicSymbolTable<ELFT> *symbolTable) { + _symbolTable = symbolTable; + } + + // The size of the section has to be determined so that fileoffsets + // may be properly assigned. Let's calculate the buckets and the chains + // and fill the chains and the buckets hash table used by the dynamic + // linker and update the filesize and memory size accordingly + void doPreFlight() override { + // The number of buckets to use for a certain number of symbols. + // If there are less than 3 symbols, 1 bucket will be used. If + // there are less than 17 symbols, 3 buckets will be used, and so + // forth. The bucket numbers are defined by GNU ld. We use the + // same rules here so we generate hash sections with the same + // size as those generated by GNU ld. + uint32_t hashBuckets[] = { 1, 3, 17, 37, 67, 97, 131, 197, 263, 521, 1031, + 2053, 4099, 8209, 16411, 32771, 65537, 131101, + 262147 }; + int hashBucketsCount = sizeof(hashBuckets) / sizeof(uint32_t); + + unsigned int bucketsCount = 0; + unsigned int dynSymCount = _entries.size(); + + // Get the number of buckes that we want to use + for (int i = 0; i < hashBucketsCount; ++i) { + if (dynSymCount < hashBuckets[i]) + break; + bucketsCount = hashBuckets[i]; + } + _buckets.resize(bucketsCount); + _chains.resize(_entries.size()); + + // Create the hash table for the dynamic linker + for (auto ai : _entries) { + unsigned int dynsymIndex = ai._index; + unsigned int bucketpos = llvm::object::elf_hash(ai._name) % bucketsCount; + _chains[dynsymIndex] = _buckets[bucketpos]; + _buckets[bucketpos] = dynsymIndex; + } + + this->_fsize = (2 + _chains.size() + _buckets.size()) * sizeof(uint32_t); + this->_msize = this->_fsize; + } + + void finalize() override { + this->_link = _symbolTable ? _symbolTable->ordinal() : 0; + if (this->_outputSection) + this->_outputSection->setLink(this->_link); + } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) override { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + uint32_t bucketChainCounts[2]; + bucketChainCounts[0] = _buckets.size(); + bucketChainCounts[1] = _chains.size(); + std::memcpy(dest, (char *)bucketChainCounts, sizeof(bucketChainCounts)); + dest += sizeof(bucketChainCounts); + // write bucket values + for (auto bi : _buckets) { + uint32_t val = (bi); + std::memcpy(dest, &val, sizeof(uint32_t)); + dest += sizeof(uint32_t); + } + // write chain values + for (auto ci : _chains) { + uint32_t val = (ci); + std::memcpy(dest, &val, sizeof(uint32_t)); + dest += sizeof(uint32_t); + } + } + +private: + std::vector<SymbolTableEntry> _entries; + std::vector<uint32_t> _buckets; + std::vector<uint32_t> _chains; + const DynamicSymbolTable<ELFT> *_symbolTable; +}; + +template <class ELFT> class EHFrameHeader : public Section<ELFT> { +public: + EHFrameHeader(const ELFLinkingContext &context, StringRef name, + TargetLayout<ELFT> &layout, int32_t order) + : Section<ELFT>(context, name, "EHFrameHeader"), _ehFrameOffset(0), + _layout(layout) { + this->setOrder(order); + this->_entSize = 0; + this->_type = SHT_PROGBITS; + this->_flags = SHF_ALLOC; + this->_alignment = ELFT::Is64Bits ? 8 : 4; + // Minimum size for empty .eh_frame_hdr. + this->_fsize = 1 + 1 + 1 + 1 + 4; + this->_msize = this->_fsize; + } + + void doPreFlight() override { + // TODO: Generate a proper binary search table. + } + + void finalize() override { + OutputSection<ELFT> *s = _layout.findOutputSection(".eh_frame"); + OutputSection<ELFT> *h = _layout.findOutputSection(".eh_frame_hdr"); + if (s && h) + _ehFrameOffset = s->virtualAddr() - (h->virtualAddr() + 4); + } + + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) override { + uint8_t *chunkBuffer = buffer.getBufferStart(); + uint8_t *dest = chunkBuffer + this->fileOffset(); + int pos = 0; + dest[pos++] = 1; // version + dest[pos++] = llvm::dwarf::DW_EH_PE_pcrel | + llvm::dwarf::DW_EH_PE_sdata4; // eh_frame_ptr_enc + dest[pos++] = llvm::dwarf::DW_EH_PE_omit; // fde_count_enc + dest[pos++] = llvm::dwarf::DW_EH_PE_omit; // table_enc + *reinterpret_cast<typename llvm::object::ELFFile<ELFT>::Elf_Sword *>( + dest + pos) = _ehFrameOffset; + } + +private: + int32_t _ehFrameOffset; + TargetLayout<ELFT> &_layout; +}; +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/SegmentChunks.h b/lib/ReaderWriter/ELF/SegmentChunks.h new file mode 100644 index 0000000000000..f2a975aaeed0b --- /dev/null +++ b/lib/ReaderWriter/ELF/SegmentChunks.h @@ -0,0 +1,686 @@ +//===- lib/ReaderWriter/ELF/SegmentChunks.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_SEGMENT_CHUNKS_H +#define LLD_READER_WRITER_ELF_SEGMENT_CHUNKS_H + +#include "Chunk.h" +#include "Layout.h" +#include "SectionChunks.h" +#include "Writer.h" +#include "lld/Core/range.h" +#include "lld/Core/Writer.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ELF.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileOutputBuffer.h" +#include <memory> + +namespace lld { +namespace elf { + +template <typename ELFT> class DefaultLayout; + +/// \brief A segment can be divided into segment slices +/// depending on how the segments can be split +template<class ELFT> +class SegmentSlice { +public: + typedef typename std::vector<Chunk<ELFT> *>::iterator SectionIter; + + SegmentSlice() { } + + /// Set the start of the slice. + void setStart(int32_t s) { _startSection = s; } + + // Set the segment slice start and end iterators. This is used to walk through + // the sections that are part of the Segment slice + void setSections(range<SectionIter> sections) { _sections = sections; } + + // Return the fileOffset of the slice + uint64_t fileOffset() const { return _offset; } + + void setFileOffset(uint64_t offset) { _offset = offset; } + + // Return the size of the slice + uint64_t fileSize() const { return _fsize; } + + void setFileSize(uint64_t filesz) { _fsize = filesz; } + + // Return the start of the slice + int32_t startSection() const { return _startSection; } + + // Return the start address of the slice + uint64_t virtualAddr() const { return _addr; } + + // Return the memory size of the slice + uint64_t memSize() const { return _memSize; } + + // Return the alignment of the slice + uint64_t alignment() const { return _alignment; } + + void setMemSize(uint64_t memsz) { _memSize = memsz; } + + void setVirtualAddr(uint64_t addr) { _addr = addr; } + + void setAlign(uint64_t align) { _alignment = align; } + + static bool compare_slices(SegmentSlice<ELFT> *a, SegmentSlice<ELFT> *b) { + return a->startSection() < b->startSection(); + } + + range<SectionIter> sections() { return _sections; } + +private: + range<SectionIter> _sections; + int32_t _startSection; + uint64_t _addr; + uint64_t _offset; + uint64_t _alignment; + uint64_t _fsize; + uint64_t _memSize; +}; + +/// \brief A segment contains a set of sections, that have similar properties +// the sections are already separated based on different flags and properties +// the segment is just a way to concatenate sections to segments +template<class ELFT> +class Segment : public Chunk<ELFT> { +public: + typedef typename std::vector<SegmentSlice<ELFT> *>::iterator SliceIter; + typedef typename std::vector<Chunk<ELFT> *>::iterator SectionIter; + + Segment(const ELFLinkingContext &context, StringRef name, + const Layout::SegmentType type); + + /// \brief the Order of segments that appear in the output file + enum SegmentOrder { + permUnknown, + permRWX, + permRX, + permR, + permRWL, + permRW, + permNonAccess + }; + + /// append a section to a segment + virtual void append(Chunk<ELFT> *chunk); + + /// Sort segments depending on the property + /// If we have a Program Header segment, it should appear first + /// If we have a INTERP segment, that should appear after the Program Header + /// All Loadable segments appear next in this order + /// All Read Write Execute segments follow + /// All Read Execute segments appear next + /// All Read only segments appear first + /// All Write execute segments follow + static bool compareSegments(Segment<ELFT> *sega, Segment<ELFT> *segb); + + /// \brief Start assigning file offset to the segment chunks The fileoffset + /// needs to be page at the start of the segment and in addition the + /// fileoffset needs to be aligned to the max section alignment within the + /// segment. This is required so that the ELF property p_poffset % p_align = + /// p_vaddr mod p_align holds true. + /// The algorithm starts off by assigning the startOffset thats passed in as + /// parameter to the first section in the segment, if the difference between + /// the newly computed offset is greater than a page, then we create a segment + /// slice, as it would be a waste of virtual memory just to be filled with + /// zeroes + void assignFileOffsets(uint64_t startOffset); + + /// \brief Assign virtual addresses to the slices + void assignVirtualAddress(uint64_t addr); + + // Write the Segment + void write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer); + + int64_t flags() const; + + /// Prepend a generic chunk to the segment. + void prepend(Chunk<ELFT> *c) { + _sections.insert(_sections.begin(), c); + } + + /// Finalize the segment before assigning File Offsets / Virtual addresses + void doPreFlight() {} + + /// Finalize the segment, before we want to write the segment header + /// information + void finalize() { + // We want to finalize the segment values for now only for non loadable + // segments, since those values are not set in the Layout + if (_segmentType == llvm::ELF::PT_LOAD) + return; + // The size is the difference of the + // last section to the first section, especially for TLS because + // the TLS segment contains both .tdata/.tbss + this->setFileOffset(_sections.front()->fileOffset()); + this->setVirtualAddr(_sections.front()->virtualAddr()); + size_t startFileOffset = _sections.front()->fileOffset(); + size_t startAddr = _sections.front()->virtualAddr(); + for (auto ai : _sections) { + this->_fsize = ai->fileOffset() + ai->fileSize() - startFileOffset; + this->_msize = ai->virtualAddr() + ai->memSize() - startAddr; + } + } + + // For LLVM RTTI + static bool classof(const Chunk<ELFT> *c) { + return c->kind() == Chunk<ELFT>::Kind::ELFSegment; + } + + // Getters + int32_t sectionCount() const { return _sections.size(); } + + /// \brief, this function returns the type of segment (PT_*) + Layout::SegmentType segmentType() { return _segmentType; } + + /// \brief return the segment type depending on the content, + /// If the content corresponds to Code, this will return Segment::Code + /// If the content corresponds to Data, this will return Segment::Data + /// If the content corresponds to TLS, this will return Segment::TLS + virtual int getContentType() const { + int64_t fl = flags(); + switch (_segmentType) { + case llvm::ELF::PT_LOAD: { + if (fl && llvm::ELF::PF_X) + return Chunk<ELFT>::ContentType::Code; + if (fl && llvm::ELF::PF_W) + return Chunk<ELFT>::ContentType::Data; + } + case llvm::ELF::PT_TLS: + return Chunk<ELFT>::ContentType::TLS; + case llvm::ELF::PT_NOTE: + return Chunk<ELFT>::ContentType::Note; + default: + return Chunk<ELFT>::ContentType::Unknown; + } + } + + int pageSize() const { return this->_context.getPageSize(); } + + int rawflags() const { return _atomflags; } + + int64_t atomflags() const { + switch (_atomflags) { + + case DefinedAtom::permUnknown: + return permUnknown; + + case DefinedAtom::permRWX: + return permRWX; + + case DefinedAtom::permR_X: + return permRX; + + case DefinedAtom::permR__: + return permR; + + case DefinedAtom::permRW_L: + return permRWL; + + case DefinedAtom::permRW_: + return permRW; + + case DefinedAtom::perm___: + default: + return permNonAccess; + } + } + + int64_t numSlices() const { return _segmentSlices.size(); } + + range<SliceIter> slices() { return _segmentSlices; } + + Chunk<ELFT> *firstSection() { return _sections[0]; } + +private: + + /// \brief Check if the chunk needs to be aligned + bool needAlign(Chunk<ELFT> *chunk) const { + if (chunk->getContentType() == Chunk<ELFT>::ContentType::Data && + _outputMagic == ELFLinkingContext::OutputMagic::NMAGIC) + return true; + return false; + } + + // Cached value of outputMagic + ELFLinkingContext::OutputMagic _outputMagic; + +protected: + /// \brief Section or some other chunk type. + std::vector<Chunk<ELFT> *> _sections; + std::vector<SegmentSlice<ELFT> *> _segmentSlices; + Layout::SegmentType _segmentType; + uint64_t _flags; + int64_t _atomflags; + llvm::BumpPtrAllocator _segmentAllocate; +}; + +/// This chunk represents a linker script expression that needs to be calculated +/// at the time the virtual addresses for the parent segment are being assigned. +template <class ELFT> class ExpressionChunk : public Chunk<ELFT> { +public: + ExpressionChunk(ELFLinkingContext &ctx, const script::SymbolAssignment *expr) + : Chunk<ELFT>(StringRef(), Chunk<ELFT>::Kind::Expression, ctx), + _expr(expr), _linkerScriptSema(ctx.linkerScriptSema()) { + this->_alignment = 1; + } + + static bool classof(const Chunk<ELFT> *c) { + return c->kind() == Chunk<ELFT>::Kind::Expression; + } + + int getContentType() const override { + return Chunk<ELFT>::ContentType::Unknown; + } + void write(ELFWriter *, TargetLayout<ELFT> &, + llvm::FileOutputBuffer &) override {} + void doPreFlight() override {} + void finalize() override {} + + std::error_code evalExpr(uint64_t &curPos) { + return _linkerScriptSema.evalExpr(_expr, curPos); + } + +private: + const script::SymbolAssignment *_expr; + script::Sema &_linkerScriptSema; +}; + +/// \brief A Program Header segment contains a set of chunks instead of sections +/// The segment doesn't contain any slice +template <class ELFT> class ProgramHeaderSegment : public Segment<ELFT> { +public: + ProgramHeaderSegment(const ELFLinkingContext &context) + : Segment<ELFT>(context, "PHDR", llvm::ELF::PT_PHDR) { + this->_alignment = 8; + this->_flags = (llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR); + } + + /// Finalize the segment, before we want to write the segment header + /// information + void finalize() { + // If the segment is of type Program Header, then the values fileOffset + // and the fileSize need to be picked up from the last section, the first + // section points to the ELF header and the second chunk points to the + // actual program headers + this->setFileOffset(this->_sections.back()->fileOffset()); + this->setVirtualAddr(this->_sections.back()->virtualAddr()); + this->_fsize = this->_sections.back()->fileSize(); + this->_msize = this->_sections.back()->memSize(); + } + +}; + +template <class ELFT> +Segment<ELFT>::Segment(const ELFLinkingContext &context, StringRef name, + const Layout::SegmentType type) + : Chunk<ELFT>(name, Chunk<ELFT>::Kind::ELFSegment, context), + _segmentType(type), _flags(0), _atomflags(0) { + this->_alignment = 0; + this->_fsize = 0; + _outputMagic = context.getOutputMagic(); +} + +// This function actually is used, but not in all instantiations of Segment. +LLVM_ATTRIBUTE_UNUSED +static DefinedAtom::ContentPermissions toAtomPerms(uint64_t flags) { + switch (flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR)) { + case SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR: + return DefinedAtom::permRWX; + case SHF_ALLOC | SHF_EXECINSTR: + return DefinedAtom::permR_X; + case SHF_ALLOC: + return DefinedAtom::permR__; + case SHF_ALLOC | SHF_WRITE: + return DefinedAtom::permRW_; + default: + return DefinedAtom::permUnknown; + } +} + +template <class ELFT> void Segment<ELFT>::append(Chunk<ELFT> *chunk) { + _sections.push_back(chunk); + Section<ELFT> *section = dyn_cast<Section<ELFT>>(chunk); + if (!section) + return; + if (_flags < section->getFlags()) + _flags |= section->getFlags(); + if (_atomflags < toAtomPerms(_flags)) + _atomflags = toAtomPerms(_flags); + if (this->_alignment < section->alignment()) + this->_alignment = section->alignment(); +} + +template <class ELFT> +bool Segment<ELFT>::compareSegments(Segment<ELFT> *sega, Segment<ELFT> *segb) { + int64_t type1 = sega->segmentType(); + int64_t type2 = segb->segmentType(); + + if (type1 == type2) + return sega->atomflags() < segb->atomflags(); + + // The single PT_PHDR segment is required to precede any loadable + // segment. We simply make it always first. + if (type1 == llvm::ELF::PT_PHDR) + return true; + if (type2 == llvm::ELF::PT_PHDR) + return false; + + // The single PT_INTERP segment is required to precede any loadable + // segment. We simply make it always second. + if (type1 == llvm::ELF::PT_INTERP) + return true; + if (type2 == llvm::ELF::PT_INTERP) + return false; + + // We then put PT_LOAD segments before any other segments. + if (type1 == llvm::ELF::PT_LOAD) + return true; + if (type2 == llvm::ELF::PT_LOAD) + return false; + + // We put the PT_GNU_RELRO segment last, because that is where the + // dynamic linker expects to find it + if (type1 == llvm::ELF::PT_GNU_RELRO) + return false; + if (type2 == llvm::ELF::PT_GNU_RELRO) + return true; + + // We put the PT_TLS segment last except for the PT_GNU_RELRO + // segment, because that is where the dynamic linker expects to find + if (type1 == llvm::ELF::PT_TLS) + return false; + if (type2 == llvm::ELF::PT_TLS) + return true; + + // Otherwise compare the types to establish an arbitrary ordering. + // FIXME: Should figure out if we should just make all other types compare + // equal, but if so, we should probably do the same for atom flags and change + // users of this to use stable_sort. + return type1 < type2; +} + +template <class ELFT> +void Segment<ELFT>::assignFileOffsets(uint64_t startOffset) { + uint64_t fileOffset = startOffset; + uint64_t curSliceFileOffset = fileOffset; + bool isDataPageAlignedForNMagic = false; + bool alignSegments = this->_context.alignSegments(); + uint64_t p_align = this->_context.getPageSize(); + uint64_t lastVirtualAddress = 0; + + this->setFileOffset(startOffset); + for (auto &slice : slices()) { + bool isFirstSection = true; + for (auto section : slice->sections()) { + // Handle linker script expressions, which may change the offset + if (!isFirstSection) + if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(section)) + fileOffset += expr->virtualAddr() - lastVirtualAddress; + // Align fileoffset to the alignment of the section. + fileOffset = llvm::RoundUpToAlignment(fileOffset, section->alignment()); + // If the linker outputmagic is set to OutputMagic::NMAGIC, align the Data + // to a page boundary + if (isFirstSection && + _outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && + _outputMagic != ELFLinkingContext::OutputMagic::OMAGIC) { + // Align to a page only if the output is not + // OutputMagic::NMAGIC/OutputMagic::OMAGIC + if (alignSegments) + fileOffset = llvm::RoundUpToAlignment(fileOffset, p_align); + else { + // Align according to ELF spec. + // in p75, http://www.sco.com/developers/devspecs/gabi41.pdf + uint64_t virtualAddress = slice->virtualAddr(); + Section<ELFT> *sect = dyn_cast<Section<ELFT>>(section); + if (sect && sect->isLoadableSection() && + ((virtualAddress & (p_align - 1)) != + (fileOffset & (p_align - 1)))) + fileOffset = llvm::RoundUpToAlignment(fileOffset, p_align) + + (virtualAddress % p_align); + } + } else if (!isDataPageAlignedForNMagic && needAlign(section)) { + fileOffset = + llvm::RoundUpToAlignment(fileOffset, this->_context.getPageSize()); + isDataPageAlignedForNMagic = true; + } + if (isFirstSection) { + slice->setFileOffset(fileOffset); + isFirstSection = false; + curSliceFileOffset = fileOffset; + } + section->setFileOffset(fileOffset); + fileOffset += section->fileSize(); + lastVirtualAddress = section->virtualAddr() + section->memSize(); + } + slice->setFileSize(fileOffset - curSliceFileOffset); + } + this->setFileSize(fileOffset - startOffset); +} + +/// \brief Assign virtual addresses to the slices +template <class ELFT> void Segment<ELFT>::assignVirtualAddress(uint64_t addr) { + int startSection = 0; + int currSection = 0; + SectionIter startSectionIter; + + // slice align is set to the max alignment of the chunks that are + // contained in the slice + uint64_t sliceAlign = 0; + // Current slice size + uint64_t curSliceSize = 0; + // Current Slice File Offset + uint64_t curSliceAddress = 0; + + startSectionIter = _sections.begin(); + startSection = 0; + bool isFirstSection = true; + bool isDataPageAlignedForNMagic = false; + uint64_t startAddr = addr; + SegmentSlice<ELFT> *slice = nullptr; + uint64_t tlsStartAddr = 0; + bool alignSegments = this->_context.alignSegments(); + StringRef prevOutputSectionName = StringRef(); + + for (auto si = _sections.begin(); si != _sections.end(); ++si) { + // If this is first section in the segment, page align the section start + // address. The linker needs to align the data section to a page boundary + // only if NMAGIC is set. + if (isFirstSection) { + isFirstSection = false; + if (alignSegments && + _outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && + _outputMagic != ELFLinkingContext::OutputMagic::OMAGIC) + // Align to a page only if the output is not + // OutputMagic::NMAGIC/OutputMagic::OMAGIC + startAddr = + llvm::RoundUpToAlignment(startAddr, this->_context.getPageSize()); + else if (!isDataPageAlignedForNMagic && needAlign(*si)) { + // If the linker outputmagic is set to OutputMagic::NMAGIC, align the + // Data to a page boundary. + startAddr = + llvm::RoundUpToAlignment(startAddr, this->_context.getPageSize()); + isDataPageAlignedForNMagic = true; + } + // align the startOffset to the section alignment + uint64_t newAddr = llvm::RoundUpToAlignment(startAddr, (*si)->alignment()); + // Handle linker script expressions, which *may update newAddr* if the + // expression assigns to "." + if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(*si)) + expr->evalExpr(newAddr); + curSliceAddress = newAddr; + sliceAlign = (*si)->alignment(); + (*si)->setVirtualAddr(curSliceAddress); + + // Handle TLS. + if (auto section = dyn_cast<Section<ELFT>>(*si)) { + if (section->getSegmentType() == llvm::ELF::PT_TLS) { + tlsStartAddr = + llvm::RoundUpToAlignment(tlsStartAddr, (*si)->alignment()); + section->assignVirtualAddress(tlsStartAddr); + tlsStartAddr += (*si)->memSize(); + } else { + section->assignVirtualAddress(newAddr); + } + } + // TBSS section is special in that it doesn't contribute to memory of any + // segment. If we see a tbss section, don't add memory size to addr The + // fileOffset is automatically taken care of since TBSS section does not + // end up using file size + if ((*si)->order() != DefaultLayout<ELFT>::ORDER_TBSS) + curSliceSize = (*si)->memSize(); + } else { + uint64_t curAddr = curSliceAddress + curSliceSize; + if (!isDataPageAlignedForNMagic && needAlign(*si)) { + // If the linker outputmagic is set to OutputMagic::NMAGIC, align the + // Data + // to a page boundary + curAddr = + llvm::RoundUpToAlignment(curAddr, this->_context.getPageSize()); + isDataPageAlignedForNMagic = true; + } + uint64_t newAddr = llvm::RoundUpToAlignment(curAddr, (*si)->alignment()); + // Handle linker script expressions, which *may update newAddr* if the + // expression assigns to "." + if (auto expr = dyn_cast<ExpressionChunk<ELFT>>(*si)) + expr->evalExpr(newAddr); + Section<ELFT> *sec = dyn_cast<Section<ELFT>>(*si); + StringRef curOutputSectionName; + if (sec) + curOutputSectionName = sec->outputSectionName(); + else { + // If this is a linker script expression, propagate the name of the + // previous section instead + if (isa<ExpressionChunk<ELFT>>(*si)) + curOutputSectionName = prevOutputSectionName; + else + curOutputSectionName = (*si)->name(); + } + bool autoCreateSlice = true; + if (curOutputSectionName == prevOutputSectionName) + autoCreateSlice = false; + // If the newAddress computed is more than a page away, let's create + // a separate segment, so that memory is not used up while running. + // Dont create a slice, if the new section falls in the same output + // section as the previous section. + if (autoCreateSlice && + ((newAddr - curAddr) > this->_context.getPageSize()) && + (_outputMagic != ELFLinkingContext::OutputMagic::NMAGIC && + _outputMagic != ELFLinkingContext::OutputMagic::OMAGIC)) { + auto sliceIter = + std::find_if(_segmentSlices.begin(), _segmentSlices.end(), + [startSection](SegmentSlice<ELFT> *s) -> bool { + return s->startSection() == startSection; + }); + if (sliceIter == _segmentSlices.end()) { + slice = new (_segmentAllocate.Allocate<SegmentSlice<ELFT>>()) + SegmentSlice<ELFT>(); + _segmentSlices.push_back(slice); + } else { + slice = (*sliceIter); + } + slice->setStart(startSection); + slice->setSections(make_range(startSectionIter, si)); + slice->setMemSize(curSliceSize); + slice->setAlign(sliceAlign); + slice->setVirtualAddr(curSliceAddress); + // Start new slice + curSliceAddress = newAddr; + (*si)->setVirtualAddr(curSliceAddress); + startSectionIter = si; + startSection = currSection; + if (auto section = dyn_cast<Section<ELFT>>(*si)) + section->assignVirtualAddress(newAddr); + curSliceSize = newAddr - curSliceAddress + (*si)->memSize(); + sliceAlign = (*si)->alignment(); + } else { + if (sliceAlign < (*si)->alignment()) + sliceAlign = (*si)->alignment(); + (*si)->setVirtualAddr(newAddr); + // Handle TLS. + if (auto section = dyn_cast<Section<ELFT>>(*si)) { + if (section->getSegmentType() == llvm::ELF::PT_TLS) { + tlsStartAddr = + llvm::RoundUpToAlignment(tlsStartAddr, (*si)->alignment()); + section->assignVirtualAddress(tlsStartAddr); + tlsStartAddr += (*si)->memSize(); + } else { + section->assignVirtualAddress(newAddr); + } + } + // TBSS section is special in that it doesn't contribute to memory of + // any segment. If we see a tbss section, don't add memory size to addr + // The fileOffset is automatically taken care of since TBSS section does + // not end up using file size. + if ((*si)->order() != DefaultLayout<ELFT>::ORDER_TBSS) + curSliceSize = newAddr - curSliceAddress + (*si)->memSize(); + else + curSliceSize = newAddr - curSliceAddress; + } + prevOutputSectionName = curOutputSectionName; + } + currSection++; + } + auto sliceIter = std::find_if(_segmentSlices.begin(), _segmentSlices.end(), + [startSection](SegmentSlice<ELFT> *s) -> bool { + return s->startSection() == startSection; + }); + if (sliceIter == _segmentSlices.end()) { + slice = new (_segmentAllocate.Allocate<SegmentSlice<ELFT>>()) + SegmentSlice<ELFT>(); + _segmentSlices.push_back(slice); + } else { + slice = (*sliceIter); + } + slice->setStart(startSection); + slice->setVirtualAddr(curSliceAddress); + slice->setMemSize(curSliceSize); + slice->setSections(make_range(startSectionIter, _sections.end())); + slice->setAlign(sliceAlign); + + // Set the segment memory size and the virtual address. + this->setMemSize(curSliceAddress - startAddr + curSliceSize); + this->setVirtualAddr(curSliceAddress); + std::stable_sort(_segmentSlices.begin(), _segmentSlices.end(), + SegmentSlice<ELFT>::compare_slices); +} + +// Write the Segment +template <class ELFT> +void Segment<ELFT>::write(ELFWriter *writer, TargetLayout<ELFT> &layout, + llvm::FileOutputBuffer &buffer) { + for (auto slice : slices()) + for (auto section : slice->sections()) + section->write(writer, layout, buffer); +} + +template<class ELFT> +int64_t +Segment<ELFT>::flags() const { + int64_t fl = 0; + if (_flags & llvm::ELF::SHF_ALLOC) + fl |= llvm::ELF::PF_R; + if (_flags & llvm::ELF::SHF_WRITE) + fl |= llvm::ELF::PF_W; + if (_flags & llvm::ELF::SHF_EXECINSTR) + fl |= llvm::ELF::PF_X; + return fl; +} +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/TODO.txt b/lib/ReaderWriter/ELF/TODO.txt new file mode 100644 index 0000000000000..90c334b781ba1 --- /dev/null +++ b/lib/ReaderWriter/ELF/TODO.txt @@ -0,0 +1,18 @@ +lib/ReaderWriter/ELF +~~~~~~~~~~~~~~~~~~~~ + +- Implement processing of DT_NEEDED elements including -rpath-link / + -rpath processing. + +- _GLOBAL_OFFSET_TABLE should be hidden and normally dropped from the output. + +- Merge SHT_NOTE sections only if applicable. + +- Do not create __got_* / __plt_* symbol table entries by default. + +- Weak references to symbols defined in a DSO should remain weak. + +- Fix section flags as they appear in input (update content permissions) + +- Check for errors in the ELFReader when creating atoms for LinkOnce + sections/Group sections. Add tests to account for the change when it happens. diff --git a/lib/ReaderWriter/ELF/TargetHandler.h b/lib/ReaderWriter/ELF/TargetHandler.h new file mode 100644 index 0000000000000..ca7a442276d18 --- /dev/null +++ b/lib/ReaderWriter/ELF/TargetHandler.h @@ -0,0 +1,86 @@ +//===- lib/ReaderWriter/ELF/TargetHandler.h -------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief These interfaces provide target specific hooks to change the linker's +/// behaivor. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_ELF_TARGET_HANDLER_H +#define LLD_READER_WRITER_ELF_TARGET_HANDLER_H + +#include "Layout.h" +#include "lld/Core/Atom.h" +#include "lld/Core/LLVM.h" +#include "lld/Core/LinkingContext.h" +#include "lld/Core/STDExtras.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileOutputBuffer.h" +#include <memory> +#include <vector> + +namespace lld { +namespace elf { +template <class ELFT> class DynamicTable; +template <class ELFT> class DynamicSymbolTable; +template <class ELFT> class ELFDefinedAtom; +template <class ELFT> class ELFReference; +class ELFWriter; +template <class ELFT> class ELFHeader; +template <class ELFT> class Section; +template <class ELFT> class TargetLayout; + +class TargetRelocationHandler { +public: + /// Constructor + TargetRelocationHandler() {} + virtual ~TargetRelocationHandler() {} + + virtual std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, + const lld::AtomLayout &, + const Reference &) const = 0; +}; + +/// \brief TargetHandler contains all the information responsible to handle a +/// a particular target on ELF. A target might wish to override implementation +/// of creating atoms and how the atoms are written to the output file. +template <class ELFT> class TargetHandler : public TargetHandlerBase { +public: + /// The layout determined completely by the Target. + virtual TargetLayout<ELFT> &getTargetLayout() = 0; + + /// Determine how relocations need to be applied. + virtual const TargetRelocationHandler &getRelocationHandler() const = 0; + + /// How does the target deal with reading input files. + virtual std::unique_ptr<Reader> getObjReader() = 0; + + /// How does the target deal with reading dynamic libraries. + virtual std::unique_ptr<Reader> getDSOReader() = 0; + + /// How does the target deal with writing ELF output. + virtual std::unique_ptr<Writer> getWriter() = 0; +}; + +inline std::error_code make_unhandled_reloc_error() { + return make_dynamic_error_code(Twine("Unhandled reference type")); +} + +inline std::error_code make_out_of_range_reloc_error() { + return make_dynamic_error_code(Twine("Relocation out of range")); +} + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/TargetLayout.h b/lib/ReaderWriter/ELF/TargetLayout.h new file mode 100644 index 0000000000000..ab7a7890a2747 --- /dev/null +++ b/lib/ReaderWriter/ELF/TargetLayout.h @@ -0,0 +1,28 @@ +//===- lib/ReaderWriter/ELF/TargetLayout.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_TARGET_LAYOUT_H +#define LLD_READER_WRITER_ELF_TARGET_LAYOUT_H + +#include "DefaultLayout.h" +#include "lld/Core/LLVM.h" + +namespace lld { +namespace elf { +/// \brief The target can override certain functions in the DefaultLayout +/// class so that the order, the name of the section and the segment type could +/// be changed in the final layout +template <class ELFT> class TargetLayout : public DefaultLayout<ELFT> { +public: + TargetLayout(ELFLinkingContext &context) : DefaultLayout<ELFT>(context) {} +}; +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/Writer.cpp b/lib/ReaderWriter/ELF/Writer.cpp new file mode 100644 index 0000000000000..3071827e07d07 --- /dev/null +++ b/lib/ReaderWriter/ELF/Writer.cpp @@ -0,0 +1,23 @@ +//===- lib/ReaderWriter/ELF/WriterELF.cpp ---------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lld/Core/Writer.h" +#include "DynamicLibraryWriter.h" +#include "ExecutableWriter.h" + +using namespace llvm; +using namespace llvm::object; + +namespace lld { + +std::unique_ptr<Writer> createWriterELF(TargetHandlerBase *handler) { + return std::move(handler->getWriter()); +} + +} // namespace lld diff --git a/lib/ReaderWriter/ELF/Writer.h b/lib/ReaderWriter/ELF/Writer.h new file mode 100644 index 0000000000000..1e819467c558f --- /dev/null +++ b/lib/ReaderWriter/ELF/Writer.h @@ -0,0 +1,38 @@ +//===- lib/ReaderWriter/ELF/Writer.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_WRITER_H +#define LLD_READER_WRITER_ELF_WRITER_H + +#include "lld/Core/File.h" +#include "lld/Core/Writer.h" + +namespace lld { +namespace elf { +/// \brief The Writer class is a base class for the linker to write +/// various kinds of ELF files. +class ELFWriter : public Writer { +public: + ELFWriter() { } + +public: + /// \brief builds the chunks that needs to be written to the output + /// ELF file + virtual void buildChunks(const File &file) = 0; + + /// \brief Writes the chunks into the output file specified by path + virtual std::error_code writeFile(const File &file, StringRef path) = 0; + + /// \brief Get the virtual address of \p atom after layout. + virtual uint64_t addressOfAtom(const Atom *atom) = 0; +}; +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/X86/CMakeLists.txt b/lib/ReaderWriter/ELF/X86/CMakeLists.txt new file mode 100644 index 0000000000000..191f7ab3d61dc --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/CMakeLists.txt @@ -0,0 +1,11 @@ +add_llvm_library(lldX86ELFTarget + X86LinkingContext.cpp + X86TargetHandler.cpp + X86RelocationHandler.cpp + LINK_LIBS + lldELF + lldReaderWriter + lldCore + LLVMObject + LLVMSupport + ) diff --git a/lib/ReaderWriter/ELF/X86/Makefile b/lib/ReaderWriter/ELF/X86/Makefile new file mode 100644 index 0000000000000..058d5133eaba2 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/Makefile @@ -0,0 +1,15 @@ +##===- lld/lib/ReaderWriter/ELF/X86/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 := lldX86ELFTarget +USEDLIBS = lldCore.a +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/X86/X86DynamicLibraryWriter.h b/lib/ReaderWriter/ELF/X86/X86DynamicLibraryWriter.h new file mode 100644 index 0000000000000..86376295bec43 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/X86DynamicLibraryWriter.h @@ -0,0 +1,67 @@ +//===- lib/ReaderWriter/ELF/X86/X86DynamicLibraryWriter.h -----------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef X86_X86_DYNAMIC_LIBRARY_WRITER_H +#define X86_X86_DYNAMIC_LIBRARY_WRITER_H + +#include "DynamicLibraryWriter.h" +#include "X86LinkingContext.h" + +namespace lld { +namespace elf { + +template <class ELFT> +class X86DynamicLibraryWriter : public DynamicLibraryWriter<ELFT> { +public: + X86DynamicLibraryWriter(X86LinkingContext &context, + X86TargetLayout<ELFT> &layout); + +protected: + // Add any runtime files and their atoms to the output + virtual bool createImplicitFiles(std::vector<std::unique_ptr<File>> &); + + virtual void finalizeDefaultAtomValues() { + return DynamicLibraryWriter<ELFT>::finalizeDefaultAtomValues(); + } + + virtual void addDefaultAtoms() { + return DynamicLibraryWriter<ELFT>::addDefaultAtoms(); + } + +private: + class GOTFile : public SimpleFile { + public: + GOTFile(const ELFLinkingContext &eti) : SimpleFile("GOTFile") {} + llvm::BumpPtrAllocator _alloc; + }; + + std::unique_ptr<GOTFile> _gotFile; + X86LinkingContext &_context; + X86TargetLayout<ELFT> &_x86Layout; +}; + +template <class ELFT> +X86DynamicLibraryWriter<ELFT>::X86DynamicLibraryWriter( + X86LinkingContext &context, X86TargetLayout<ELFT> &layout) + : DynamicLibraryWriter<ELFT>(context, layout), + _gotFile(new GOTFile(context)), _context(context), _x86Layout(layout) {} + +template <class ELFT> +bool X86DynamicLibraryWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + DynamicLibraryWriter<ELFT>::createImplicitFiles(result); + _gotFile->addAtom(*new (_gotFile->_alloc) GLOBAL_OFFSET_TABLEAtom(*_gotFile)); + _gotFile->addAtom(*new (_gotFile->_alloc) DYNAMICAtom(*_gotFile)); + result.push_back(std::move(_gotFile)); + return true; +} + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/X86/X86ELFFile.h b/lib/ReaderWriter/ELF/X86/X86ELFFile.h new file mode 100644 index 0000000000000..621c38c435054 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/X86ELFFile.h @@ -0,0 +1,41 @@ +//===- lib/ReaderWriter/ELF/X86/X86ELFFile.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_X86_X86_ELF_FILE_H +#define LLD_READER_WRITER_ELF_X86_X86_ELF_FILE_H + +#include "ELFReader.h" + +namespace lld { +namespace elf { + +class X86LinkingContext; + +template <class ELFT> class X86ELFFile : public ELFFile<ELFT> { +public: + X86ELFFile(std::unique_ptr<MemoryBuffer> mb, X86LinkingContext &ctx) + : ELFFile<ELFT>(std::move(mb), ctx) {} + + static ErrorOr<std::unique_ptr<X86ELFFile>> + create(std::unique_ptr<MemoryBuffer> mb, X86LinkingContext &ctx) { + return std::unique_ptr<X86ELFFile<ELFT>>( + new X86ELFFile<ELFT>(std::move(mb), ctx)); + } +}; + +template <class ELFT> class X86DynamicFile : public DynamicFile<ELFT> { +public: + X86DynamicFile(const X86LinkingContext &context, StringRef name) + : DynamicFile<ELFT>(context, name) {} +}; + +} // elf +} // lld + +#endif // LLD_READER_WRITER_ELF_X86_X86_ELF_FILE_H diff --git a/lib/ReaderWriter/ELF/X86/X86ELFReader.h b/lib/ReaderWriter/ELF/X86/X86ELFReader.h new file mode 100644 index 0000000000000..96186c5eb0248 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/X86ELFReader.h @@ -0,0 +1,62 @@ +//===- lib/ReaderWriter/ELF/X86/X86ELFReader.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_X86_X86_ELF_READER_H +#define LLD_READER_WRITER_X86_X86_ELF_READER_H + +#include "ELFReader.h" +#include "X86ELFFile.h" + +namespace lld { +namespace elf { + +typedef llvm::object::ELFType<llvm::support::little, 2, false> X86ELFType; + +struct X86DynamicFileCreateELFTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::SharedLibraryFile>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + X86LinkingContext &ctx) { + return lld::elf::X86DynamicFile<ELFT>::create(std::move(mb), ctx); + } +}; + +struct X86ELFFileCreateELFTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::File>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + X86LinkingContext &ctx) { + return lld::elf::X86ELFFile<ELFT>::create(std::move(mb), ctx); + } +}; + +class X86ELFObjectReader + : public ELFObjectReader<X86ELFType, X86ELFFileCreateELFTraits, + X86LinkingContext> { +public: + X86ELFObjectReader(X86LinkingContext &ctx) + : ELFObjectReader<X86ELFType, X86ELFFileCreateELFTraits, + X86LinkingContext>(ctx, llvm::ELF::EM_386) {} +}; + +class X86ELFDSOReader + : public ELFDSOReader<X86ELFType, X86DynamicFileCreateELFTraits, + X86LinkingContext> { +public: + X86ELFDSOReader(X86LinkingContext &ctx) + : ELFDSOReader<X86ELFType, X86DynamicFileCreateELFTraits, + X86LinkingContext>(ctx, llvm::ELF::EM_386) {} +}; + +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_X86_X86_ELF_READER_H diff --git a/lib/ReaderWriter/ELF/X86/X86ExecutableWriter.h b/lib/ReaderWriter/ELF/X86/X86ExecutableWriter.h new file mode 100644 index 0000000000000..68acc06c2261e --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/X86ExecutableWriter.h @@ -0,0 +1,57 @@ +//===- lib/ReaderWriter/ELF/X86/X86ExecutableWriter.h ---------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef X86_X86_EXECUTABLE_WRITER_H +#define X86_X86_EXECUTABLE_WRITER_H + +#include "ExecutableWriter.h" +#include "X86LinkingContext.h" + +namespace lld { +namespace elf { + +template <class ELFT> +class X86ExecutableWriter : public ExecutableWriter<ELFT> { +public: + X86ExecutableWriter(X86LinkingContext &context, + X86TargetLayout<ELFT> &layout); + +protected: + // Add any runtime files and their atoms to the output + virtual bool createImplicitFiles(std::vector<std::unique_ptr<File>> &); + + virtual void finalizeDefaultAtomValues() { + return ExecutableWriter<ELFT>::finalizeDefaultAtomValues(); + } + + virtual void addDefaultAtoms() { + return ExecutableWriter<ELFT>::addDefaultAtoms(); + } + +private: + X86LinkingContext &_context; + X86TargetLayout<ELFT> &_x86Layout; +}; + +template <class ELFT> +X86ExecutableWriter<ELFT>::X86ExecutableWriter(X86LinkingContext &context, + X86TargetLayout<ELFT> &layout) + : ExecutableWriter<ELFT>(context, layout), _context(context), + _x86Layout(layout) {} + +template <class ELFT> +bool X86ExecutableWriter<ELFT>::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + ExecutableWriter<ELFT>::createImplicitFiles(result); + return true; +} + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/X86/X86LinkingContext.cpp b/lib/ReaderWriter/ELF/X86/X86LinkingContext.cpp new file mode 100644 index 0000000000000..26d715cf29534 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/X86LinkingContext.cpp @@ -0,0 +1,28 @@ +//===- lib/ReaderWriter/ELF/X86/X86LinkingContext.cpp ---------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "X86LinkingContext.h" +#include "X86TargetHandler.h" +#include "lld/Core/LLVM.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/ErrorOr.h" + +using namespace lld; + +std::unique_ptr<ELFLinkingContext> +elf::X86LinkingContext::create(llvm::Triple triple) { + if (triple.getArch() == llvm::Triple::x86) + return std::unique_ptr<ELFLinkingContext>( + new elf::X86LinkingContext(triple)); + return nullptr; +} + +elf::X86LinkingContext::X86LinkingContext(llvm::Triple triple) + : ELFLinkingContext(triple, std::unique_ptr<TargetHandlerBase>( + new X86TargetHandler(*this))) {} diff --git a/lib/ReaderWriter/ELF/X86/X86LinkingContext.h b/lib/ReaderWriter/ELF/X86/X86LinkingContext.h new file mode 100644 index 0000000000000..ff424f411aae5 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/X86LinkingContext.h @@ -0,0 +1,42 @@ +//===- lib/ReaderWriter/ELF/X86/X86LinkingContext.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_X86_TARGETINFO_H +#define LLD_READER_WRITER_ELF_X86_TARGETINFO_H + +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/ELF.h" + +namespace lld { +namespace elf { +class X86LinkingContext final : public ELFLinkingContext { +public: + static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + X86LinkingContext(llvm::Triple); + + /// \brief X86 has only two relative relocation + /// a) for supporting IFUNC relocs - R_386_IRELATIVE + /// b) for supporting relative relocs - R_386_RELATIVE + bool isRelativeReloc(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::x86); + switch (r.kindValue()) { + case llvm::ELF::R_386_IRELATIVE: + case llvm::ELF::R_386_RELATIVE: + return true; + default: + return false; + } + } +}; +} // end namespace elf +} // end namespace lld +#endif diff --git a/lib/ReaderWriter/ELF/X86/X86RelocationHandler.cpp b/lib/ReaderWriter/ELF/X86/X86RelocationHandler.cpp new file mode 100644 index 0000000000000..da5a24c6ec37d --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/X86RelocationHandler.cpp @@ -0,0 +1,57 @@ +//===- lib/ReaderWriter/ELF/X86/X86RelocationHandler.cpp ------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "X86LinkingContext.h" +#include "X86TargetHandler.h" +#include "llvm/Support/Endian.h" + +using namespace lld; +using namespace lld::elf; +using namespace llvm::support::endian; + +namespace { +/// \brief R_386_32 - word32: S + A +static int reloc32(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { + int32_t result = (uint32_t)(S + A); + write32le(location, result | read32le(location)); + return 0; +} + +/// \brief R_386_PC32 - word32: S + A - P +static int relocPC32(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { + uint32_t result = (uint32_t)((S + A) - P); + write32le(location, result + read32le(location)); + return 0; +} +} + +std::error_code X86TargetRelocationHandler::applyRelocation( + ELFWriter &writer, llvm::FileOutputBuffer &buf, const lld::AtomLayout &atom, + const Reference &ref) const { + uint8_t *atomContent = buf.getBufferStart() + atom._fileOffset; + uint8_t *location = atomContent + ref.offsetInAtom(); + uint64_t targetVAddress = writer.addressOfAtom(ref.target()); + uint64_t relocVAddress = atom._virtualAddr + ref.offsetInAtom(); + + if (ref.kindNamespace() != Reference::KindNamespace::ELF) + return std::error_code(); + assert(ref.kindArch() == Reference::KindArch::x86); + switch (ref.kindValue()) { + case R_386_32: + reloc32(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_386_PC32: + relocPC32(location, relocVAddress, targetVAddress, ref.addend()); + break; + default: + return make_unhandled_reloc_error(); + } + + return std::error_code(); +} diff --git a/lib/ReaderWriter/ELF/X86/X86RelocationHandler.h b/lib/ReaderWriter/ELF/X86/X86RelocationHandler.h new file mode 100644 index 0000000000000..f161cdd55983d --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/X86RelocationHandler.h @@ -0,0 +1,29 @@ +//===- lib/ReaderWriter/ELF/X86/X86RelocationHandler.h --------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef X86_X86_RELOCATION_HANDLER_H +#define X86_X86_RELOCATION_HANDLER_H + +#include "X86TargetHandler.h" + +namespace lld { +namespace elf { +typedef llvm::object::ELFType<llvm::support::little, 2, false> X86ELFType; + +class X86TargetRelocationHandler final : public TargetRelocationHandler { +public: + std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, + const lld::AtomLayout &, + const Reference &) const override; +}; + +} // end namespace elf +} // end namespace lld + +#endif // X86_X86_RELOCATION_HANDLER_H diff --git a/lib/ReaderWriter/ELF/X86/X86TargetHandler.cpp b/lib/ReaderWriter/ELF/X86/X86TargetHandler.cpp new file mode 100644 index 0000000000000..22d9182314248 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/X86TargetHandler.cpp @@ -0,0 +1,53 @@ +//===- lib/ReaderWriter/ELF/X86/X86TargetHandler.cpp ----------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "X86TargetHandler.h" +#include "X86DynamicLibraryWriter.h" +#include "X86ExecutableWriter.h" +#include "X86LinkingContext.h" +#include "X86RelocationHandler.h" + +using namespace lld; +using namespace elf; + +using namespace llvm::ELF; + +std::unique_ptr<Writer> X86TargetHandler::getWriter() { + switch (_x86LinkingContext.getOutputELFType()) { + case llvm::ELF::ET_EXEC: + return std::unique_ptr<Writer>(new X86ExecutableWriter<X86ELFType>( + _x86LinkingContext, *_x86TargetLayout.get())); + case llvm::ELF::ET_DYN: + return std::unique_ptr<Writer>(new X86DynamicLibraryWriter<X86ELFType>( + _x86LinkingContext, *_x86TargetLayout.get())); + case llvm::ELF::ET_REL: + llvm_unreachable("TODO: support -r mode"); + default: + llvm_unreachable("unsupported output type"); + } +} + +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), + +const Registry::KindStrings X86TargetHandler::kindStrings[] = { +#include "llvm/Support/ELFRelocs/i386.def" + LLD_KIND_STRING_END +}; + +#undef ELF_RELOC + +void X86TargetHandler::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, Reference::KindArch::x86, + kindStrings); +} + +X86TargetHandler::X86TargetHandler(X86LinkingContext &context) + : _x86LinkingContext(context), + _x86TargetLayout(new X86TargetLayout<X86ELFType>(context)), + _x86RelocationHandler(new X86TargetRelocationHandler()) {} diff --git a/lib/ReaderWriter/ELF/X86/X86TargetHandler.h b/lib/ReaderWriter/ELF/X86/X86TargetHandler.h new file mode 100644 index 0000000000000..6c40267354192 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86/X86TargetHandler.h @@ -0,0 +1,63 @@ +//===- lib/ReaderWriter/ELF/X86/X86TargetHandler.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_X86_TARGET_HANDLER_H +#define LLD_READER_WRITER_ELF_X86_TARGET_HANDLER_H + +#include "DefaultTargetHandler.h" +#include "TargetLayout.h" +#include "X86ELFFile.h" +#include "X86ELFReader.h" +#include "X86RelocationHandler.h" + +namespace lld { +namespace elf { + +class X86LinkingContext; + +template <class ELFT> class X86TargetLayout : public TargetLayout<ELFT> { +public: + X86TargetLayout(X86LinkingContext &context) : TargetLayout<ELFT>(context) {} +}; + +class X86TargetHandler final + : public DefaultTargetHandler<X86ELFType> { +public: + X86TargetHandler(X86LinkingContext &context); + + X86TargetLayout<X86ELFType> &getTargetLayout() override { + return *(_x86TargetLayout.get()); + } + + void registerRelocationNames(Registry ®istry) override; + + const X86TargetRelocationHandler &getRelocationHandler() const override { + return *(_x86RelocationHandler.get()); + } + + std::unique_ptr<Reader> getObjReader() override { + return std::unique_ptr<Reader>(new X86ELFObjectReader(_x86LinkingContext)); + } + + std::unique_ptr<Reader> getDSOReader() override { + return std::unique_ptr<Reader>(new X86ELFDSOReader(_x86LinkingContext)); + } + + std::unique_ptr<Writer> getWriter() override; + +protected: + static const Registry::KindStrings kindStrings[]; + X86LinkingContext &_x86LinkingContext; + std::unique_ptr<X86TargetLayout<X86ELFType>> _x86TargetLayout; + std::unique_ptr<X86TargetRelocationHandler> _x86RelocationHandler; +}; +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/X86_64/CMakeLists.txt b/lib/ReaderWriter/ELF/X86_64/CMakeLists.txt new file mode 100644 index 0000000000000..a85d2b5046306 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/CMakeLists.txt @@ -0,0 +1,16 @@ +add_llvm_library(lldX86_64ELFTarget + X86_64LinkingContext.cpp + X86_64TargetHandler.cpp + X86_64RelocationHandler.cpp + X86_64RelocationPass.cpp + LINK_LIBS + lldELF + lldReaderWriter + lldCore + LLVMObject + LLVMSupport + ) + +include_directories(.) + +add_subdirectory(ExampleSubTarget) diff --git a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/CMakeLists.txt b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/CMakeLists.txt new file mode 100644 index 0000000000000..d13c98008e55f --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/CMakeLists.txt @@ -0,0 +1,11 @@ +add_llvm_library(lldExampleSubTarget + ExampleLinkingContext.cpp + ExampleTargetHandler.cpp + LINK_LIBS + lldX86_64ELFTarget + lldELF + lldReaderWriter + lldCore + LLVMObject + LLVMSupport + ) diff --git a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleLinkingContext.cpp b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleLinkingContext.cpp new file mode 100644 index 0000000000000..dbbb3ad3bc90d --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleLinkingContext.cpp @@ -0,0 +1,35 @@ +//===- lib/ReaderWriter/ELF/X86_64/ExampleTarget/ExampleLinkingContext.cpp ----===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ExampleLinkingContext.h" +#include "ExampleTargetHandler.h" + +using namespace lld; +using namespace elf; + +std::unique_ptr<ELFLinkingContext> +ExampleLinkingContext::create(llvm::Triple triple) { + if (triple.getVendorName() == "example") + return llvm::make_unique<ExampleLinkingContext>(triple); + return nullptr; +} + +ExampleLinkingContext::ExampleLinkingContext(llvm::Triple triple) + : X86_64LinkingContext(triple, std::unique_ptr<TargetHandlerBase>( + new ExampleTargetHandler(*this))) { + _outputELFType = llvm::ELF::ET_LOPROC; +} + +StringRef ExampleLinkingContext::entrySymbolName() const { + return "_start"; +} + +void ExampleLinkingContext::addPasses(PassManager &p) { + ELFLinkingContext::addPasses(p); +} diff --git a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleLinkingContext.h b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleLinkingContext.h new file mode 100644 index 0000000000000..5bb11cd35b41e --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleLinkingContext.h @@ -0,0 +1,31 @@ +//===- lib/ReaderWriter/ELF/X86_64/ExampleTarget/ExampleLinkingContext.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_X86_64_EXAMPLE_TARGET_EXAMPLE_LINKING_CONTEXT +#define LLD_READER_WRITER_ELF_X86_64_EXAMPLE_TARGET_EXAMPLE_LINKING_CONTEXT + +#include "X86_64LinkingContext.h" +#include "X86_64TargetHandler.h" + +namespace lld { +namespace elf { + +class ExampleLinkingContext final : public X86_64LinkingContext { +public: + static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + ExampleLinkingContext(llvm::Triple triple); + + StringRef entrySymbolName() const override; + void addPasses(PassManager &) override; +}; + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.cpp b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.cpp new file mode 100644 index 0000000000000..b66b0d903f6a4 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.cpp @@ -0,0 +1,23 @@ +//===- lib/ReaderWriter/ELF/X86_64/ExampleTarget/ExampleTargetHandler.cpp -===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ExampleTargetHandler.h" +#include "X86_64ExecutableWriter.h" +#include "ExampleLinkingContext.h" + +using namespace lld; +using namespace elf; + +ExampleTargetHandler::ExampleTargetHandler(ExampleLinkingContext &c) + : X86_64TargetHandler(c), _exampleContext(c) {} + +std::unique_ptr<Writer> ExampleTargetHandler::getWriter() { + return std::unique_ptr<Writer>( + new X86_64ExecutableWriter(_exampleContext, *_x86_64TargetLayout)); +} diff --git a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.h b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.h new file mode 100644 index 0000000000000..19a642113359f --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/ExampleTargetHandler.h @@ -0,0 +1,31 @@ +//===- lib/ReaderWriter/ELF/X86_64/ExampleTarget/ExampleTargetHandler.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_X86_64_EXAMPLE_TARGET_EXAMPLE_TARGET_HANDLER_H +#define LLD_READER_WRITER_ELF_X86_64_EXAMPLE_TARGET_EXAMPLE_TARGET_HANDLER_H + +#include "X86_64TargetHandler.h" + +namespace lld { +namespace elf { +class ExampleLinkingContext; + +class ExampleTargetHandler final : public X86_64TargetHandler { +public: + ExampleTargetHandler(ExampleLinkingContext &c); + + std::unique_ptr<Writer> getWriter() override; + +private: + ExampleLinkingContext &_exampleContext; +}; +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/Makefile b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/Makefile new file mode 100644 index 0000000000000..8f0b0fead1f69 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/ExampleSubTarget/Makefile @@ -0,0 +1,15 @@ +##===- lld/lib/ReaderWriter/ELF/X86_64/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 := lldExampleSubTarget +USEDLIBS = lldX86_64ELFTarget.a +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/X86_64/Makefile b/lib/ReaderWriter/ELF/X86_64/Makefile new file mode 100644 index 0000000000000..dbeb4d2270507 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/Makefile @@ -0,0 +1,19 @@ +##===- lld/lib/ReaderWriter/ELF/X86_64/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 := lldX86_64ELFTarget +USEDLIBS = lldCore.a + +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF +CPP.Flags += -I$(PROJ_SRC_DIR)/$(LLD_LEVEL)/lib/ReaderWriter/ELF/X86_64/ + +PARALLEL_DIRS := ExampleSubTarget + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/ELF/X86_64/TODO.rst b/lib/ReaderWriter/ELF/X86_64/TODO.rst new file mode 100644 index 0000000000000..a2411a00d1ea1 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/TODO.rst @@ -0,0 +1,46 @@ +ELF x86-64 +~~~~~~~~~~ + +Unimplemented Features +###################### + +* Code models other than the small code model +* TLS strength reduction + +Unimplemented Relocations +######################### + +All of these relocations are defined in: +http://www.x86-64.org/documentation/abi.pdf + +Trivial Relocs +<<<<<<<<<<<<<< + +These are very simple relocation calculations to implement. +See lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp + +* R_X86_64_8 +* R_X86_64_PC8 +* R_X86_64_SIZE32 +* R_X86_64_SIZE64 +* R_X86_64_GOTPC32 (this relocation requires there to be a __GLOBAL_OFFSET_TABLE__) + +Global Offset Table Relocs +<<<<<<<<<<<<<<<<<<<<<<<<<< + +* R_X86_64_GOTOFF32 +* R_X86_64_GOTOFF64 + +Global Dynamic Thread Local Storage Relocs +<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + +These relocations take more effort to implement, but some of them are done. +Their implementation lives in lib/ReaderWriter/ELF/X86_64/{X86_64RelocationPass.cpp,X86_64RelocationHandler.cpp}. + +Documentation on these relocations can be found in: +http://www.akkadia.org/drepper/tls.pdf +http://www.fsfla.org/~lxoliva/writeups/TLS/RFC-TLSDESC-x86.txt + +* R_X86_64_GOTPC32_TLSDESC +* R_X86_64_TLSDESC_CALL +* R_X86_64_TLSDESC diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64DynamicLibraryWriter.h b/lib/ReaderWriter/ELF/X86_64/X86_64DynamicLibraryWriter.h new file mode 100644 index 0000000000000..b996186115b66 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64DynamicLibraryWriter.h @@ -0,0 +1,63 @@ +//===- lib/ReaderWriter/ELF/X86/X86_64DynamicLibraryWriter.h ---------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef X86_64_DYNAMIC_LIBRARY_WRITER_H +#define X86_64_DYNAMIC_LIBRARY_WRITER_H + +#include "DynamicLibraryWriter.h" +#include "X86_64ElfType.h" +#include "X86_64LinkingContext.h" +#include "X86_64TargetHandler.h" + +namespace lld { +namespace elf { + +class X86_64DynamicLibraryWriter : public DynamicLibraryWriter<X86_64ELFType> { +public: + X86_64DynamicLibraryWriter(X86_64LinkingContext &context, + X86_64TargetLayout &layout); + +protected: + // Add any runtime files and their atoms to the output + virtual bool createImplicitFiles(std::vector<std::unique_ptr<File>> &); + + virtual void finalizeDefaultAtomValues() { + return DynamicLibraryWriter::finalizeDefaultAtomValues(); + } + + virtual void addDefaultAtoms() { + return DynamicLibraryWriter::addDefaultAtoms(); + } + +private: + class GOTFile : public SimpleFile { + public: + GOTFile(const ELFLinkingContext &eti) : SimpleFile("GOTFile") {} + llvm::BumpPtrAllocator _alloc; + }; + + std::unique_ptr<GOTFile> _gotFile; +}; + +X86_64DynamicLibraryWriter::X86_64DynamicLibraryWriter( + X86_64LinkingContext &context, X86_64TargetLayout &layout) + : DynamicLibraryWriter(context, layout), _gotFile(new GOTFile(context)) {} + +bool X86_64DynamicLibraryWriter::createImplicitFiles( + std::vector<std::unique_ptr<File>> &result) { + DynamicLibraryWriter::createImplicitFiles(result); + _gotFile->addAtom(*new (_gotFile->_alloc) GLOBAL_OFFSET_TABLEAtom(*_gotFile)); + _gotFile->addAtom(*new (_gotFile->_alloc) DYNAMICAtom(*_gotFile)); + result.push_back(std::move(_gotFile)); + return true; +} + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64ELFFile.h b/lib/ReaderWriter/ELF/X86_64/X86_64ELFFile.h new file mode 100644 index 0000000000000..d43840a63e7e0 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64ELFFile.h @@ -0,0 +1,41 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64ELFFile.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_X86_64_ELF_FILE_H +#define LLD_READER_WRITER_ELF_X86_64_ELF_FILE_H + +#include "ELFReader.h" + +namespace lld { +namespace elf { + +class X86_64LinkingContext; + +template <class ELFT> class X86_64ELFFile : public ELFFile<ELFT> { +public: + X86_64ELFFile(std::unique_ptr<MemoryBuffer> mb, X86_64LinkingContext &ctx) + : ELFFile<ELFT>(std::move(mb), ctx) {} + + static ErrorOr<std::unique_ptr<X86_64ELFFile>> + create(std::unique_ptr<MemoryBuffer> mb, X86_64LinkingContext &ctx) { + return std::unique_ptr<X86_64ELFFile<ELFT>>( + new X86_64ELFFile<ELFT>(std::move(mb), ctx)); + } +}; + +template <class ELFT> class X86_64DynamicFile : public DynamicFile<ELFT> { +public: + X86_64DynamicFile(const X86_64LinkingContext &context, StringRef name) + : DynamicFile<ELFT>(context, name) {} +}; + +} // elf +} // lld + +#endif // LLD_READER_WRITER_ELF_X86_64_ELF_FILE_H diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64ELFReader.h b/lib/ReaderWriter/ELF/X86_64/X86_64ELFReader.h new file mode 100644 index 0000000000000..9b1284c6dfa8c --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64ELFReader.h @@ -0,0 +1,62 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64ELFReader.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_X86_64_X86_64_ELF_READER_H +#define LLD_READER_WRITER_X86_64_X86_64_ELF_READER_H + +#include "ELFReader.h" +#include "X86_64ELFFile.h" + +namespace lld { +namespace elf { + +typedef llvm::object::ELFType<llvm::support::little, 2, true> X86_64ELFType; + +struct X86_64DynamicFileCreateELFTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::SharedLibraryFile>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + X86_64LinkingContext &ctx) { + return lld::elf::X86_64DynamicFile<ELFT>::create(std::move(mb), ctx); + } +}; + +struct X86_64ELFFileCreateELFTraits { + typedef llvm::ErrorOr<std::unique_ptr<lld::File>> result_type; + + template <class ELFT> + static result_type create(std::unique_ptr<llvm::MemoryBuffer> mb, + X86_64LinkingContext &ctx) { + return lld::elf::X86_64ELFFile<ELFT>::create(std::move(mb), ctx); + } +}; + +class X86_64ELFObjectReader + : public ELFObjectReader<X86_64ELFType, X86_64ELFFileCreateELFTraits, + X86_64LinkingContext> { +public: + X86_64ELFObjectReader(X86_64LinkingContext &ctx) + : ELFObjectReader<X86_64ELFType, X86_64ELFFileCreateELFTraits, + X86_64LinkingContext>(ctx, llvm::ELF::EM_X86_64) {} +}; + +class X86_64ELFDSOReader + : public ELFDSOReader<X86_64ELFType, X86_64DynamicFileCreateELFTraits, + X86_64LinkingContext> { +public: + X86_64ELFDSOReader(X86_64LinkingContext &ctx) + : ELFDSOReader<X86_64ELFType, X86_64DynamicFileCreateELFTraits, + X86_64LinkingContext>(ctx, llvm::ELF::EM_X86_64) {} +}; + +} // namespace elf +} // namespace lld + +#endif // LLD_READER_WRITER_ELF_X86_64_X86_64_READER_H diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64ElfType.h b/lib/ReaderWriter/ELF/X86_64/X86_64ElfType.h new file mode 100644 index 0000000000000..0b982e7754e21 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64ElfType.h @@ -0,0 +1,21 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64ElfType.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_X86_64_X86_64_ELF_TYPE_H +#define LLD_READER_WRITER_ELF_X86_64_X86_64_ELF_TYPE_H + +#include "llvm/Object/ELF.h" + +namespace lld { +namespace elf { +typedef llvm::object::ELFType<llvm::support::little, 2, true> X86_64ELFType; +} +} + +#endif diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64ExecutableWriter.h b/lib/ReaderWriter/ELF/X86_64/X86_64ExecutableWriter.h new file mode 100644 index 0000000000000..f549ed6dcfcbb --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64ExecutableWriter.h @@ -0,0 +1,61 @@ +//===- lib/ReaderWriter/ELF/X86/X86_64ExecutableWriter.h ------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef X86_64_EXECUTABLE_WRITER_H +#define X86_64_EXECUTABLE_WRITER_H + +#include "ExecutableWriter.h" +#include "X86_64ElfType.h" +#include "X86_64LinkingContext.h" + +namespace lld { +namespace elf { + +class X86_64ExecutableWriter : public ExecutableWriter<X86_64ELFType> { +public: + X86_64ExecutableWriter(X86_64LinkingContext &context, + X86_64TargetLayout &layout) + : ExecutableWriter(context, layout), _gotFile(new GOTFile(context)), + _context(context) {} + +protected: + // Add any runtime files and their atoms to the output + virtual bool + createImplicitFiles(std::vector<std::unique_ptr<File>> &result) { + ExecutableWriter::createImplicitFiles(result); + _gotFile->addAtom(*new (_gotFile->_alloc) + GLOBAL_OFFSET_TABLEAtom(*_gotFile)); + if (_context.isDynamic()) + _gotFile->addAtom(*new (_gotFile->_alloc) DYNAMICAtom(*_gotFile)); + result.push_back(std::move(_gotFile)); + return true; + } + + virtual void finalizeDefaultAtomValues() { + return ExecutableWriter::finalizeDefaultAtomValues(); + } + + virtual void addDefaultAtoms() { + return ExecutableWriter::addDefaultAtoms(); + } + +private: + class GOTFile : public SimpleFile { + public: + GOTFile(const ELFLinkingContext &eti) : SimpleFile("GOTFile") {} + llvm::BumpPtrAllocator _alloc; + }; + + std::unique_ptr<GOTFile> _gotFile; + X86_64LinkingContext &_context; +}; + +} // namespace elf +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.cpp b/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.cpp new file mode 100644 index 0000000000000..6a8ce8bd64967 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.cpp @@ -0,0 +1,38 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.cpp ---------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "X86_64LinkingContext.h" +#include "X86_64RelocationPass.h" +#include "X86_64TargetHandler.h" + +using namespace lld; +using namespace elf; + +X86_64LinkingContext::X86_64LinkingContext( + llvm::Triple triple, std::unique_ptr<TargetHandlerBase> handler) + : ELFLinkingContext(triple, std::move(handler)) {} + +X86_64LinkingContext::X86_64LinkingContext(llvm::Triple triple) + : X86_64LinkingContext(triple, + llvm::make_unique<X86_64TargetHandler>(*this)) {} + +void X86_64LinkingContext::addPasses(PassManager &pm) { + auto pass = createX86_64RelocationPass(*this); + if (pass) + pm.add(std::move(pass)); + ELFLinkingContext::addPasses(pm); +} + +std::unique_ptr<ELFLinkingContext> +X86_64LinkingContext::create(llvm::Triple triple) { + if (triple.getArch() == llvm::Triple::x86_64) + return std::unique_ptr<ELFLinkingContext>( + new elf::X86_64LinkingContext(triple)); + return nullptr; +} diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.h b/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.h new file mode 100644 index 0000000000000..2cc799a9c8102 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.h @@ -0,0 +1,100 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64LinkingContext.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_X86_64_X86_64_LINKING_CONTEXT_H +#define LLD_READER_WRITER_ELF_X86_64_X86_64_LINKING_CONTEXT_H + +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/ELF.h" + +namespace lld { +namespace elf { + +/// \brief x86-64 internal references. +enum { + /// \brief The 32 bit index of the relocation in the got this reference refers + /// to. + LLD_R_X86_64_GOTRELINDEX = 1024, +}; + +class X86_64LinkingContext : public ELFLinkingContext { +protected: + X86_64LinkingContext(llvm::Triple, std::unique_ptr<TargetHandlerBase>); +public: + static std::unique_ptr<ELFLinkingContext> create(llvm::Triple); + X86_64LinkingContext(llvm::Triple); + + void addPasses(PassManager &) override; + + uint64_t getBaseAddress() const override { + if (_baseAddress == 0) + return 0x400000; + return _baseAddress; + } + + bool isDynamicRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::x86_64); + switch (r.kindValue()) { + case llvm::ELF::R_X86_64_RELATIVE: + case llvm::ELF::R_X86_64_GLOB_DAT: + case llvm::ELF::R_X86_64_COPY: + case llvm::ELF::R_X86_64_DTPMOD64: + case llvm::ELF::R_X86_64_DTPOFF64: + case llvm::ELF::R_X86_64_TPOFF64: + return true; + default: + return false; + } + } + + bool isCopyRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::x86_64); + if (r.kindValue() == llvm::ELF::R_X86_64_COPY) + return true; + return false; + } + + virtual bool isPLTRelocation(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::x86_64); + switch (r.kindValue()) { + case llvm::ELF::R_X86_64_JUMP_SLOT: + case llvm::ELF::R_X86_64_IRELATIVE: + return true; + default: + return false; + } + } + + /// \brief X86_64 has two relative relocations + /// a) for supporting IFUNC - R_X86_64_IRELATIVE + /// b) for supporting relative relocs - R_X86_64_RELATIVE + bool isRelativeReloc(const Reference &r) const override { + if (r.kindNamespace() != Reference::KindNamespace::ELF) + return false; + assert(r.kindArch() == Reference::KindArch::x86_64); + switch (r.kindValue()) { + case llvm::ELF::R_X86_64_IRELATIVE: + case llvm::ELF::R_X86_64_RELATIVE: + return true; + default: + return false; + } + } +}; +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp new file mode 100644 index 0000000000000..8fd74f43bbd28 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp @@ -0,0 +1,151 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.cpp ------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "X86_64LinkingContext.h" +#include "X86_64TargetHandler.h" +#include "llvm/Support/Endian.h" + +using namespace lld; +using namespace lld::elf; +using namespace llvm::support::endian; + +/// \brief R_X86_64_64 - word64: S + A +static void reloc64(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { + uint64_t result = S + A; + write64le(location, result | read64le(location)); +} + +/// \brief R_X86_64_PC32 - word32: S + A - P +static void relocPC32(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { + uint32_t result = (uint32_t)((S + A) - P); + write32le(location, result + read32le(location)); +} + +/// \brief R_X86_64_32 - word32: S + A +static void reloc32(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { + int32_t result = (uint32_t)(S + A); + write32le(location, result | read32le(location)); + // TODO: Make sure that the result zero extends to the 64bit value. +} + +/// \brief R_X86_64_32S - word32: S + A +static void reloc32S(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { + int32_t result = (int32_t)(S + A); + write32le(location, result | read32le(location)); + // TODO: Make sure that the result sign extends to the 64bit value. +} + +/// \brief R_X86_64_16 - word16: S + A +static void reloc16(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { + uint16_t result = (uint16_t)(S + A); + write16le(location, result | read16le(location)); + // TODO: Check for overflow. +} + +/// \brief R_X86_64_PC16 - word16: S + A - P +static void relocPC16(uint8_t *location, uint64_t P, uint64_t S, int64_t A) { + uint16_t result = (uint16_t)((S + A) - P); + write16le(location, result | read16le(location)); + // TODO: Check for overflow. +} + +/// \brief R_X86_64_PC64 - word64: S + A - P +static void relocPC64(uint8_t *location, uint64_t P, uint64_t S, uint64_t A) { + int64_t result = (uint64_t)((S + A) - P); + write64le(location, result | read64le(location)); +} + +std::error_code X86_64TargetRelocationHandler::applyRelocation( + ELFWriter &writer, llvm::FileOutputBuffer &buf, const lld::AtomLayout &atom, + const Reference &ref) const { + uint8_t *atomContent = buf.getBufferStart() + atom._fileOffset; + uint8_t *location = atomContent + ref.offsetInAtom(); + uint64_t targetVAddress = writer.addressOfAtom(ref.target()); + uint64_t relocVAddress = atom._virtualAddr + ref.offsetInAtom(); + + if (ref.kindNamespace() != Reference::KindNamespace::ELF) + return std::error_code(); + assert(ref.kindArch() == Reference::KindArch::x86_64); + switch (ref.kindValue()) { + case R_X86_64_NONE: + break; + case R_X86_64_64: + reloc64(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_X86_64_PC32: + case R_X86_64_GOTPCREL: + relocPC32(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_X86_64_32: + reloc32(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_X86_64_32S: + reloc32S(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_X86_64_16: + reloc16(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_X86_64_PC16: + relocPC16(location, relocVAddress, targetVAddress, ref.addend()); + break; + case R_X86_64_TPOFF64: + case R_X86_64_DTPOFF32: + case R_X86_64_TPOFF32: { + _tlsSize = _x86_64Layout.getTLSSize(); + if (ref.kindValue() == R_X86_64_TPOFF32 || + ref.kindValue() == R_X86_64_DTPOFF32) { + write32le(location, targetVAddress - _tlsSize); + } else { + write64le(location, targetVAddress - _tlsSize); + } + break; + } + case R_X86_64_TLSGD: { + relocPC32(location, relocVAddress, targetVAddress, ref.addend()); + break; + } + case R_X86_64_TLSLD: { + // Rewrite to move %fs:0 into %rax. Technically we should verify that the + // next relocation is a PC32 to __tls_get_addr... + static uint8_t instr[] = { 0x66, 0x66, 0x66, 0x64, 0x48, 0x8b, 0x04, 0x25, + 0x00, 0x00, 0x00, 0x00 }; + std::memcpy(location - 3, instr, sizeof(instr)); + break; + } + case R_X86_64_PC64: + relocPC64(location, relocVAddress, targetVAddress, ref.addend()); + break; + case LLD_R_X86_64_GOTRELINDEX: { + const DefinedAtom *target = cast<const DefinedAtom>(ref.target()); + for (const Reference *r : *target) { + if (r->kindValue() == R_X86_64_JUMP_SLOT) { + uint32_t index; + if (!_x86_64Layout.getPLTRelocationTable()->getRelocationIndex(*r, + index)) + llvm_unreachable("Relocation doesn't exist"); + reloc32(location, 0, index, 0); + break; + } + } + break; + } + // Runtime only relocations. Ignore here. + case R_X86_64_RELATIVE: + case R_X86_64_IRELATIVE: + case R_X86_64_JUMP_SLOT: + case R_X86_64_GLOB_DAT: + case R_X86_64_DTPMOD64: + case R_X86_64_DTPOFF64: + break; + default: + return make_unhandled_reloc_error(); + } + + return std::error_code(); +} diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.h b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.h new file mode 100644 index 0000000000000..9e2c2171015d1 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.h @@ -0,0 +1,39 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64RelocationHandler.h --------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef X86_64_RELOCATION_HANDLER_H +#define X86_64_RELOCATION_HANDLER_H + +#include "X86_64TargetHandler.h" + +namespace lld { +namespace elf { +typedef llvm::object::ELFType<llvm::support::little, 2, true> X86_64ELFType; + +class X86_64TargetLayout; + +class X86_64TargetRelocationHandler final : public TargetRelocationHandler { +public: + X86_64TargetRelocationHandler(X86_64TargetLayout &layout) + : _tlsSize(0), _x86_64Layout(layout) {} + + std::error_code applyRelocation(ELFWriter &, llvm::FileOutputBuffer &, + const lld::AtomLayout &, + const Reference &) const override; + +private: + // Cached size of the TLS segment. + mutable uint64_t _tlsSize; + X86_64TargetLayout &_x86_64Layout; +}; + +} // end namespace elf +} // end namespace lld + +#endif // X86_64_RELOCATION_HANDLER_H diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.cpp b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.cpp new file mode 100644 index 0000000000000..0703927fd56c3 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.cpp @@ -0,0 +1,513 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.cpp ---------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Defines the relocation processing pass for x86-64. This includes +/// GOT and PLT entries, TLS, COPY, and ifunc. +/// +/// This is based on section 4.4.1 of the AMD64 ABI (no stable URL as of Oct, +/// 2013). +/// +/// This also includes aditional behaivor that gnu-ld and gold implement but +/// which is not specified anywhere. +/// +//===----------------------------------------------------------------------===// + +#include "X86_64RelocationPass.h" +#include "Atoms.h" +#include "X86_64LinkingContext.h" +#include "lld/Core/Simple.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" + +using namespace lld; +using namespace lld::elf; +using namespace llvm::ELF; + +// .got values +static const uint8_t x86_64GotAtomContent[8] = {0}; + +// .plt value (entry 0) +static const uint8_t x86_64Plt0AtomContent[16] = { + 0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // pushq GOT+8(%rip) + 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *GOT+16(%rip) + 0x90, 0x90, 0x90, 0x90 // nopnopnop +}; + +// .plt values (other entries) +static const uint8_t x86_64PltAtomContent[16] = { + 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmpq *gotatom(%rip) + 0x68, 0x00, 0x00, 0x00, 0x00, // pushq reloc-index + 0xe9, 0x00, 0x00, 0x00, 0x00 // jmpq plt[-1] +}; + +// TLS GD Entry +static const uint8_t x86_64GotTlsGdAtomContent[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +namespace { +/// \brief Atoms that are used by X86_64 dynamic linking +class X86_64GOTAtom : public GOTAtom { +public: + X86_64GOTAtom(const File &f, StringRef secName) : GOTAtom(f, secName) {} + + ArrayRef<uint8_t> rawContent() const override { + return ArrayRef<uint8_t>(x86_64GotAtomContent, 8); + } +}; + +/// \brief X86_64 GOT TLS GD entry. +class GOTTLSGdAtom : public X86_64GOTAtom { +public: + GOTTLSGdAtom(const File &f, StringRef secName) : X86_64GOTAtom(f, secName) {} + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(x86_64GotTlsGdAtomContent); + } +}; + +class X86_64PLT0Atom : public PLT0Atom { +public: + X86_64PLT0Atom(const File &f) : PLT0Atom(f) {} + ArrayRef<uint8_t> rawContent() const override { + return ArrayRef<uint8_t>(x86_64Plt0AtomContent, 16); + } +}; + +class X86_64PLTAtom : public PLTAtom { +public: + X86_64PLTAtom(const File &f, StringRef secName) : PLTAtom(f, secName) {} + + ArrayRef<uint8_t> rawContent() const override { + return ArrayRef<uint8_t>(x86_64PltAtomContent, 16); + } +}; + +class ELFPassFile : public SimpleFile { +public: + ELFPassFile(const ELFLinkingContext &eti) : SimpleFile("ELFPassFile") { + setOrdinal(eti.getNextOrdinalAndIncrement()); + } + + llvm::BumpPtrAllocator _alloc; +}; + +/// \brief CRTP base for handling relocations. +template <class Derived> class RelocationPass : public Pass { + /// \brief Handle a specific reference. + void handleReference(const DefinedAtom &atom, const Reference &ref) { + if (ref.kindNamespace() != Reference::KindNamespace::ELF) + return; + assert(ref.kindArch() == Reference::KindArch::x86_64); + switch (ref.kindValue()) { + case R_X86_64_16: + case R_X86_64_32: + case R_X86_64_32S: + case R_X86_64_64: + case R_X86_64_PC16: + case R_X86_64_PC32: + case R_X86_64_PC64: + static_cast<Derived *>(this)->handlePlain(ref); + break; + case R_X86_64_PLT32: + static_cast<Derived *>(this)->handlePLT32(ref); + break; + case R_X86_64_GOT32: + case R_X86_64_GOTPC32: + case R_X86_64_GOTPCREL: + case R_X86_64_GOTOFF64: + static_cast<Derived *>(this)->handleGOT(ref); + break; + case R_X86_64_GOTTPOFF: // GOT Thread Pointer Offset + static_cast<Derived *>(this)->handleGOTTPOFF(ref); + break; + case R_X86_64_TLSGD: + static_cast<Derived *>(this)->handleTLSGd(ref); + break; + } + } + +protected: + /// \brief get the PLT entry for a given IFUNC Atom. + /// + /// If the entry does not exist. Both the GOT and PLT entry is created. + const PLTAtom *getIFUNCPLTEntry(const DefinedAtom *da) { + auto plt = _pltMap.find(da); + if (plt != _pltMap.end()) + return plt->second; + auto ga = new (_file._alloc) X86_64GOTAtom(_file, ".got.plt"); + ga->addReferenceELF_x86_64(R_X86_64_IRELATIVE, 0, da, 0); + auto pa = new (_file._alloc) X86_64PLTAtom(_file, ".plt"); + pa->addReferenceELF_x86_64(R_X86_64_PC32, 2, ga, -4); +#ifndef NDEBUG + ga->_name = "__got_ifunc_"; + ga->_name += da->name(); + pa->_name = "__plt_ifunc_"; + pa->_name += da->name(); +#endif + _gotMap[da] = ga; + _pltMap[da] = pa; + _gotVector.push_back(ga); + _pltVector.push_back(pa); + return pa; + } + + /// \brief Redirect the call to the PLT stub for the target IFUNC. + /// + /// This create a PLT and GOT entry for the IFUNC if one does not exist. The + /// GOT entry and a IRELATIVE relocation to the original target resolver. + std::error_code handleIFUNC(const Reference &ref) { + auto target = dyn_cast_or_null<const DefinedAtom>(ref.target()); + if (target && target->contentType() == DefinedAtom::typeResolver) + const_cast<Reference &>(ref).setTarget(getIFUNCPLTEntry(target)); + return std::error_code(); + } + + /// \brief Create a GOT entry for the TP offset of a TLS atom. + const GOTAtom *getGOTTPOFF(const Atom *atom) { + auto got = _gotMap.find(atom); + if (got == _gotMap.end()) { + auto g = new (_file._alloc) X86_64GOTAtom(_file, ".got"); + g->addReferenceELF_x86_64(R_X86_64_TPOFF64, 0, atom, 0); +#ifndef NDEBUG + g->_name = "__got_tls_"; + g->_name += atom->name(); +#endif + _gotMap[atom] = g; + _gotVector.push_back(g); + return g; + } + return got->second; + } + + /// \brief Create a TPOFF64 GOT entry and change the relocation to a PC32 to + /// the GOT. + void handleGOTTPOFF(const Reference &ref) { + const_cast<Reference &>(ref).setTarget(getGOTTPOFF(ref.target())); + const_cast<Reference &>(ref).setKindValue(R_X86_64_PC32); + } + + /// \brief Create a TLS GOT entry with DTPMOD64/DTPOFF64 dynamic relocations. + void handleTLSGd(const Reference &ref) { + const_cast<Reference &>(ref).setTarget(getTLSGdGOTEntry(ref.target())); + } + + /// \brief Create a GOT entry containing 0. + const GOTAtom *getNullGOT() { + if (!_null) { + _null = new (_file._alloc) X86_64GOTAtom(_file, ".got.plt"); +#ifndef NDEBUG + _null->_name = "__got_null"; +#endif + } + return _null; + } + + const GOTAtom *getGOT(const DefinedAtom *da) { + auto got = _gotMap.find(da); + if (got == _gotMap.end()) { + auto g = new (_file._alloc) X86_64GOTAtom(_file, ".got"); + g->addReferenceELF_x86_64(R_X86_64_64, 0, da, 0); +#ifndef NDEBUG + g->_name = "__got_"; + g->_name += da->name(); +#endif + _gotMap[da] = g; + _gotVector.push_back(g); + return g; + } + return got->second; + } + + const GOTAtom *getTLSGdGOTEntry(const Atom *a) { + auto got = _gotTLSGdMap.find(a); + if (got != _gotTLSGdMap.end()) + return got->second; + + auto ga = new (_file._alloc) GOTTLSGdAtom(_file, ".got"); + _gotTLSGdMap[a] = ga; + + _tlsGotVector.push_back(ga); + ga->addReferenceELF_x86_64(R_X86_64_DTPMOD64, 0, a, 0); + ga->addReferenceELF_x86_64(R_X86_64_DTPOFF64, 8, a, 0); + + return ga; + } + +public: + RelocationPass(const ELFLinkingContext &ctx) + : _file(ctx), _ctx(ctx), _null(nullptr), _PLT0(nullptr), _got0(nullptr), + _got1(nullptr) {} + + /// \brief Do the pass. + /// + /// The goal here is to first process each reference individually. Each call + /// to handleReference may modify the reference itself and/or create new + /// atoms which must be stored in one of the maps below. + /// + /// After all references are handled, the atoms created during that are all + /// added to mf. + void perform(std::unique_ptr<MutableFile> &mf) override { + ScopedTask task(getDefaultDomain(), "X86-64 GOT/PLT Pass"); + // Process all references. + for (const auto &atom : mf->defined()) + for (const auto &ref : *atom) + handleReference(*atom, *ref); + + // Add all created atoms to the link. + uint64_t ordinal = 0; + if (_PLT0) { + _PLT0->setOrdinal(ordinal++); + mf->addAtom(*_PLT0); + } + for (auto &plt : _pltVector) { + plt->setOrdinal(ordinal++); + mf->addAtom(*plt); + } + if (_null) { + _null->setOrdinal(ordinal++); + mf->addAtom(*_null); + } + if (_PLT0) { + _got0->setOrdinal(ordinal++); + _got1->setOrdinal(ordinal++); + mf->addAtom(*_got0); + mf->addAtom(*_got1); + } + for (auto &got : _gotVector) { + got->setOrdinal(ordinal++); + mf->addAtom(*got); + } + for (auto &got : _tlsGotVector) { + got->setOrdinal(ordinal++); + mf->addAtom(*got); + } + for (auto obj : _objectVector) { + obj->setOrdinal(ordinal++); + mf->addAtom(*obj); + } + } + +protected: + /// \brief Owner of all the Atoms created by this pass. + ELFPassFile _file; + const ELFLinkingContext &_ctx; + + /// \brief Map Atoms to their GOT entries. + llvm::DenseMap<const Atom *, GOTAtom *> _gotMap; + + /// \brief Map Atoms to their PLT entries. + llvm::DenseMap<const Atom *, PLTAtom *> _pltMap; + + /// \brief Map Atoms to TLS GD GOT entries. + llvm::DenseMap<const Atom *, GOTAtom *> _gotTLSGdMap; + + /// \brief Map Atoms to their Object entries. + llvm::DenseMap<const Atom *, ObjectAtom *> _objectMap; + + /// \brief the list of GOT/PLT atoms + std::vector<GOTAtom *> _gotVector; + std::vector<PLTAtom *> _pltVector; + std::vector<ObjectAtom *> _objectVector; + + /// \brief the list of TLS GOT atoms. + std::vector<GOTAtom *> _tlsGotVector; + + /// \brief GOT entry that is always 0. Used for undefined weaks. + GOTAtom *_null; + + /// \brief The got and plt entries for .PLT0. This is used to call into the + /// dynamic linker for symbol resolution. + /// @{ + PLT0Atom *_PLT0; + GOTAtom *_got0; + GOTAtom *_got1; + /// @} +}; + +/// This implements the static relocation model. Meaning GOT and PLT entries are +/// not created for references that can be directly resolved. These are +/// converted to a direct relocation. For entries that do require a GOT or PLT +/// entry, that entry is statically bound. +/// +/// TLS always assumes module 1 and attempts to remove indirection. +class StaticRelocationPass final + : public RelocationPass<StaticRelocationPass> { +public: + StaticRelocationPass(const elf::X86_64LinkingContext &ctx) + : RelocationPass(ctx) {} + + std::error_code handlePlain(const Reference &ref) { return handleIFUNC(ref); } + + std::error_code handlePLT32(const Reference &ref) { + // __tls_get_addr is handled elsewhere. + if (ref.target() && ref.target()->name() == "__tls_get_addr") { + const_cast<Reference &>(ref).setKindValue(R_X86_64_NONE); + return std::error_code(); + } + // Static code doesn't need PLTs. + const_cast<Reference &>(ref).setKindValue(R_X86_64_PC32); + // Handle IFUNC. + if (const DefinedAtom *da = + dyn_cast_or_null<const DefinedAtom>(ref.target())) + if (da->contentType() == DefinedAtom::typeResolver) + return handleIFUNC(ref); + return std::error_code(); + } + + std::error_code handleGOT(const Reference &ref) { + if (isa<UndefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getNullGOT()); + else if (const DefinedAtom *da = dyn_cast<const DefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getGOT(da)); + return std::error_code(); + } +}; + +class DynamicRelocationPass final + : public RelocationPass<DynamicRelocationPass> { +public: + DynamicRelocationPass(const elf::X86_64LinkingContext &ctx) + : RelocationPass(ctx) {} + + const PLT0Atom *getPLT0() { + if (_PLT0) + return _PLT0; + // Fill in the null entry. + getNullGOT(); + _PLT0 = new (_file._alloc) X86_64PLT0Atom(_file); + _got0 = new (_file._alloc) X86_64GOTAtom(_file, ".got.plt"); + _got1 = new (_file._alloc) X86_64GOTAtom(_file, ".got.plt"); + _PLT0->addReferenceELF_x86_64(R_X86_64_PC32, 2, _got0, -4); + _PLT0->addReferenceELF_x86_64(R_X86_64_PC32, 8, _got1, -4); +#ifndef NDEBUG + _got0->_name = "__got0"; + _got1->_name = "__got1"; +#endif + return _PLT0; + } + + const PLTAtom *getPLTEntry(const Atom *a) { + auto plt = _pltMap.find(a); + if (plt != _pltMap.end()) + return plt->second; + auto ga = new (_file._alloc) X86_64GOTAtom(_file, ".got.plt"); + ga->addReferenceELF_x86_64(R_X86_64_JUMP_SLOT, 0, a, 0); + auto pa = new (_file._alloc) X86_64PLTAtom(_file, ".plt"); + pa->addReferenceELF_x86_64(R_X86_64_PC32, 2, ga, -4); + pa->addReferenceELF_x86_64(LLD_R_X86_64_GOTRELINDEX, 7, ga, 0); + pa->addReferenceELF_x86_64(R_X86_64_PC32, 12, getPLT0(), -4); + // Set the starting address of the got entry to the second instruction in + // the plt entry. + ga->addReferenceELF_x86_64(R_X86_64_64, 0, pa, 6); +#ifndef NDEBUG + ga->_name = "__got_"; + ga->_name += a->name(); + pa->_name = "__plt_"; + pa->_name += a->name(); +#endif + _gotMap[a] = ga; + _pltMap[a] = pa; + _gotVector.push_back(ga); + _pltVector.push_back(pa); + return pa; + } + + const ObjectAtom *getObjectEntry(const SharedLibraryAtom *a) { + auto obj = _objectMap.find(a); + if (obj != _objectMap.end()) + return obj->second; + + auto oa = new (_file._alloc) ObjectAtom(_file); + // This needs to point to the atom that we just created. + oa->addReferenceELF_x86_64(R_X86_64_COPY, 0, oa, 0); + + oa->_name = a->name(); + oa->_size = a->size(); + + _objectMap[a] = oa; + _objectVector.push_back(oa); + return oa; + } + + std::error_code handlePlain(const Reference &ref) { + if (!ref.target()) + return std::error_code(); + if (auto sla = dyn_cast<SharedLibraryAtom>(ref.target())) { + if (sla->type() == SharedLibraryAtom::Type::Data) + const_cast<Reference &>(ref).setTarget(getObjectEntry(sla)); + else if (sla->type() == SharedLibraryAtom::Type::Code) + const_cast<Reference &>(ref).setTarget(getPLTEntry(sla)); + } else + return handleIFUNC(ref); + return std::error_code(); + } + + std::error_code handlePLT32(const Reference &ref) { + // Turn this into a PC32 to the PLT entry. + const_cast<Reference &>(ref).setKindValue(R_X86_64_PC32); + // Handle IFUNC. + if (const DefinedAtom *da = + dyn_cast_or_null<const DefinedAtom>(ref.target())) + if (da->contentType() == DefinedAtom::typeResolver) + return handleIFUNC(ref); + // If it is undefined at link time, push the work to the dynamic linker by + // creating a PLT entry + if (isa<SharedLibraryAtom>(ref.target()) || + isa<UndefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getPLTEntry(ref.target())); + return std::error_code(); + } + + const GOTAtom *getSharedGOT(const Atom *a) { + auto got = _gotMap.find(a); + if (got == _gotMap.end()) { + auto g = new (_file._alloc) X86_64GOTAtom(_file, ".got"); + g->addReferenceELF_x86_64(R_X86_64_GLOB_DAT, 0, a, 0); +#ifndef NDEBUG + g->_name = "__got_"; + g->_name += a->name(); +#endif + _gotMap[a] = g; + _gotVector.push_back(g); + return g; + } + return got->second; + } + + std::error_code handleGOT(const Reference &ref) { + if (const DefinedAtom *da = dyn_cast<const DefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getGOT(da)); + // Handle undefined atoms in the same way as shared lib atoms: to be + // resolved at run time. + else if (isa<SharedLibraryAtom>(ref.target()) || + isa<UndefinedAtom>(ref.target())) + const_cast<Reference &>(ref).setTarget(getSharedGOT(ref.target())); + return std::error_code(); + } +}; +} // end anon namespace + +std::unique_ptr<Pass> +lld::elf::createX86_64RelocationPass(const X86_64LinkingContext &ctx) { + switch (ctx.getOutputELFType()) { + case llvm::ELF::ET_EXEC: + if (ctx.isDynamic()) + return llvm::make_unique<DynamicRelocationPass>(ctx); + return llvm::make_unique<StaticRelocationPass>(ctx); + case llvm::ELF::ET_DYN: + return llvm::make_unique<DynamicRelocationPass>(ctx); + case llvm::ELF::ET_REL: + return nullptr; + default: + llvm_unreachable("Unhandled output file type"); + } +} diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.h b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.h new file mode 100644 index 0000000000000..1635b5e5f57b1 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.h @@ -0,0 +1,32 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64RelocationPass.h -----------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Declares the relocation processing pass for x86-64. This includes +/// GOT and PLT entries, TLS, COPY, and ifunc. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_ELF_X86_64_X86_64_RELOCATION_PASS_H +#define LLD_READER_WRITER_ELF_X86_64_X86_64_RELOCATION_PASS_H + +#include <memory> + +namespace lld { +class Pass; +namespace elf { +class X86_64LinkingContext; + +/// \brief Create x86-64 relocation pass for the given linking context. +std::unique_ptr<Pass> +createX86_64RelocationPass(const X86_64LinkingContext &); +} +} + +#endif diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.cpp b/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.cpp new file mode 100644 index 0000000000000..f35330eb25c05 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.cpp @@ -0,0 +1,52 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.cpp ----------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Atoms.h" +#include "X86_64DynamicLibraryWriter.h" +#include "X86_64ExecutableWriter.h" +#include "X86_64LinkingContext.h" +#include "X86_64TargetHandler.h" + +using namespace lld; +using namespace elf; + +X86_64TargetHandler::X86_64TargetHandler(X86_64LinkingContext &context) + : _context(context), _x86_64TargetLayout(new X86_64TargetLayout(context)), + _x86_64RelocationHandler( + new X86_64TargetRelocationHandler(*_x86_64TargetLayout.get())) {} + +void X86_64TargetHandler::registerRelocationNames(Registry ®istry) { + registry.addKindTable(Reference::KindNamespace::ELF, + Reference::KindArch::x86_64, kindStrings); +} + +std::unique_ptr<Writer> X86_64TargetHandler::getWriter() { + switch (this->_context.getOutputELFType()) { + case llvm::ELF::ET_EXEC: + return std::unique_ptr<Writer>( + new X86_64ExecutableWriter(_context, *_x86_64TargetLayout.get())); + case llvm::ELF::ET_DYN: + return std::unique_ptr<Writer>( + new X86_64DynamicLibraryWriter(_context, *_x86_64TargetLayout.get())); + case llvm::ELF::ET_REL: + llvm_unreachable("TODO: support -r mode"); + default: + llvm_unreachable("unsupported output type"); + } +} + +#define ELF_RELOC(name, value) LLD_KIND_STRING_ENTRY(name), + +const Registry::KindStrings X86_64TargetHandler::kindStrings[] = { +#include "llvm/Support/ELFRelocs/x86_64.def" + LLD_KIND_STRING_ENTRY(LLD_R_X86_64_GOTRELINDEX), + LLD_KIND_STRING_END +}; + +#undef ELF_RELOC diff --git a/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.h b/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.h new file mode 100644 index 0000000000000..57da7bca01e67 --- /dev/null +++ b/lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.h @@ -0,0 +1,69 @@ +//===- lib/ReaderWriter/ELF/X86_64/X86_64TargetHandler.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_X86_64_X86_64_TARGET_HANDLER_H +#define LLD_READER_WRITER_ELF_X86_64_X86_64_TARGET_HANDLER_H + +#include "DefaultTargetHandler.h" +#include "TargetLayout.h" +#include "X86_64ELFFile.h" +#include "X86_64ELFReader.h" +#include "X86_64LinkingContext.h" +#include "X86_64RelocationHandler.h" +#include "lld/Core/Simple.h" + +namespace lld { +namespace elf { +class X86_64TargetLayout : public TargetLayout<X86_64ELFType> { +public: + X86_64TargetLayout(X86_64LinkingContext &context) + : TargetLayout(context) {} + + void finalizeOutputSectionLayout() override { + sortOutputSectionByPriority(".init_array", ".init_array"); + sortOutputSectionByPriority(".fini_array", ".fini_array"); + } +}; + +class X86_64TargetHandler + : public DefaultTargetHandler<X86_64ELFType> { +public: + X86_64TargetHandler(X86_64LinkingContext &context); + + X86_64TargetLayout &getTargetLayout() override { + return *(_x86_64TargetLayout.get()); + } + + void registerRelocationNames(Registry ®istry) override; + + const X86_64TargetRelocationHandler &getRelocationHandler() const override { + return *(_x86_64RelocationHandler.get()); + } + + std::unique_ptr<Reader> getObjReader() override { + return std::unique_ptr<Reader>(new X86_64ELFObjectReader(_context)); + } + + std::unique_ptr<Reader> getDSOReader() override { + return std::unique_ptr<Reader>(new X86_64ELFDSOReader(_context)); + } + + std::unique_ptr<Writer> getWriter() override; + +protected: + static const Registry::KindStrings kindStrings[]; + X86_64LinkingContext &_context; + std::unique_ptr<X86_64TargetLayout> _x86_64TargetLayout; + std::unique_ptr<X86_64TargetRelocationHandler> _x86_64RelocationHandler; +}; + +} // end namespace elf +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/FileArchive.cpp b/lib/ReaderWriter/FileArchive.cpp new file mode 100644 index 0000000000000..3f38814ae18e1 --- /dev/null +++ b/lib/ReaderWriter/FileArchive.cpp @@ -0,0 +1,293 @@ +//===- lib/ReaderWriter/FileArchive.cpp -----------------------------------===// +// +// 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/LLVM.h" +#include "lld/Core/LinkingContext.h" +#include "lld/Core/Parallel.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MemoryBuffer.h" +#include <memory> +#include <mutex> +#include <set> +#include <unordered_map> + +using llvm::object::Archive; +using llvm::object::ObjectFile; +using llvm::object::SymbolRef; +using llvm::object::symbol_iterator; +using llvm::object::object_error; + +namespace lld { + +namespace { + +/// \brief The FileArchive class represents an Archive Library file +class FileArchive : public lld::ArchiveLibraryFile { +public: + FileArchive(std::unique_ptr<MemoryBuffer> mb, const Registry ®, + StringRef path, bool logLoading) + : ArchiveLibraryFile(path), _mb(std::shared_ptr<MemoryBuffer>(mb.release())), + _registry(reg), _logLoading(logLoading) {} + + /// \brief Check if any member of the archive contains an Atom with the + /// specified name and return the File object for that member, or nullptr. + File *find(StringRef name, bool dataSymbolOnly) override { + auto member = _symbolMemberMap.find(name); + if (member == _symbolMemberMap.end()) + return nullptr; + Archive::child_iterator ci = member->second; + + // Don't return a member already returned + const char *memberStart = ci->getBuffer().data(); + if (_membersInstantiated.count(memberStart)) + return nullptr; + if (dataSymbolOnly && !isDataSymbol(ci, name)) + return nullptr; + + _membersInstantiated.insert(memberStart); + + // Check if a file is preloaded. + { + std::lock_guard<std::mutex> lock(_mutex); + auto it = _preloaded.find(memberStart); + if (it != _preloaded.end()) { + std::unique_ptr<Future<File *>> &p = it->second; + Future<File *> *future = p.get(); + return future->get(); + } + } + + std::unique_ptr<File> result; + if (instantiateMember(ci, result)) + return nullptr; + + // give up the pointer so that this object no longer manages it + return result.release(); + } + + // Instantiate a member file containing a given symbol name. + void preload(TaskGroup &group, StringRef name) override { + auto member = _symbolMemberMap.find(name); + if (member == _symbolMemberMap.end()) + return; + Archive::child_iterator ci = member->second; + + // Do nothing if a member is already instantiated. + const char *memberStart = ci->getBuffer().data(); + if (_membersInstantiated.count(memberStart)) + return; + + std::lock_guard<std::mutex> lock(_mutex); + if (_preloaded.find(memberStart) != _preloaded.end()) + return; + + // Instantiate the member + auto *future = new Future<File *>(); + _preloaded[memberStart] = std::unique_ptr<Future<File *>>(future); + + group.spawn([=] { + std::unique_ptr<File> result; + std::error_code ec = instantiateMember(ci, result); + future->set(ec ? nullptr : result.release()); + }); + } + + /// \brief parse each member + std::error_code + parseAllMembers(std::vector<std::unique_ptr<File>> &result) override { + if (std::error_code ec = parse()) + return ec; + for (auto mf = _archive->child_begin(), me = _archive->child_end(); + mf != me; ++mf) { + std::unique_ptr<File> file; + if (std::error_code ec = instantiateMember(mf, file)) + return ec; + result.push_back(std::move(file)); + } + return std::error_code(); + } + + 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; + } + + /// Returns a set of all defined symbols in the archive. + std::set<StringRef> getDefinedSymbols() override { + parse(); + std::set<StringRef> ret; + for (const auto &e : _symbolMemberMap) + ret.insert(e.first); + return ret; + } + +protected: + std::error_code doParse() override { + // Make Archive object which will be owned by FileArchive object. + std::error_code ec; + _archive.reset(new Archive(_mb->getMemBufferRef(), ec)); + if (ec) + return ec; + if ((ec = buildTableOfContents())) + return ec; + return std::error_code(); + } + +private: + std::error_code + instantiateMember(Archive::child_iterator member, + std::unique_ptr<File> &result) const { + ErrorOr<llvm::MemoryBufferRef> mbOrErr = member->getMemoryBufferRef(); + if (std::error_code ec = mbOrErr.getError()) + return ec; + llvm::MemoryBufferRef mb = mbOrErr.get(); + std::string memberPath = (_archive->getFileName() + "(" + + mb.getBufferIdentifier() + ")").str(); + + if (_logLoading) + llvm::errs() << memberPath << "\n"; + + std::unique_ptr<MemoryBuffer> memberMB(MemoryBuffer::getMemBuffer( + mb.getBuffer(), mb.getBufferIdentifier(), false)); + + std::vector<std::unique_ptr<File>> files; + if (std::error_code ec = _registry.loadFile(std::move(memberMB), files)) + return ec; + assert(files.size() == 1); + result = std::move(files[0]); + if (std::error_code ec = result->parse()) + return ec; + result->setArchivePath(_archive->getFileName()); + + // The memory buffer is co-owned by the archive file and the children, + // so that the bufffer is deallocated when all the members are destructed. + result->setSharedMemoryBuffer(_mb); + return std::error_code(); + } + + // Parses the given memory buffer as an object file, and returns true + // code if the given symbol is a data symbol. If the symbol is not a data + // symbol or does not exist, returns false. + bool isDataSymbol(Archive::child_iterator member, StringRef symbol) const { + ErrorOr<llvm::MemoryBufferRef> buf = member->getMemoryBufferRef(); + if (buf.getError()) + return false; + std::unique_ptr<MemoryBuffer> mb(MemoryBuffer::getMemBuffer( + buf.get().getBuffer(), buf.get().getBufferIdentifier(), false)); + + auto objOrErr(ObjectFile::createObjectFile(mb->getMemBufferRef())); + if (objOrErr.getError()) + return false; + std::unique_ptr<ObjectFile> obj = std::move(objOrErr.get()); + + for (SymbolRef sym : obj->symbols()) { + // Skip until we find the symbol. + StringRef name; + if (sym.getName(name)) + return false; + if (name != symbol) + continue; + uint32_t flags = sym.getFlags(); + if (flags <= SymbolRef::SF_Undefined) + continue; + + // Returns true if it's a data symbol. + SymbolRef::Type type; + if (sym.getType(type)) + return false; + if (type == SymbolRef::ST_Data) + return true; + } + return false; + } + + std::error_code buildTableOfContents() { + DEBUG_WITH_TYPE("FileArchive", llvm::dbgs() + << "Table of contents for archive '" + << _archive->getFileName() << "':\n"); + for (const Archive::Symbol &sym : _archive->symbols()) { + StringRef name = sym.getName(); + ErrorOr<Archive::child_iterator> memberOrErr = sym.getMember(); + if (std::error_code ec = memberOrErr.getError()) + return ec; + Archive::child_iterator member = memberOrErr.get(); + DEBUG_WITH_TYPE( + "FileArchive", + llvm::dbgs() << llvm::format("0x%08llX ", member->getBuffer().data()) + << "'" << name << "'\n"); + _symbolMemberMap[name] = member; + } + return std::error_code(); + } + + typedef std::unordered_map<StringRef, Archive::child_iterator> MemberMap; + typedef std::set<const char *> InstantiatedSet; + + std::shared_ptr<MemoryBuffer> _mb; + const Registry &_registry; + std::unique_ptr<Archive> _archive; + MemberMap _symbolMemberMap; + InstantiatedSet _membersInstantiated; + atom_collection_vector<DefinedAtom> _definedAtoms; + atom_collection_vector<UndefinedAtom> _undefinedAtoms; + atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms; + atom_collection_vector<AbsoluteAtom> _absoluteAtoms; + bool _logLoading; + std::vector<std::unique_ptr<MemoryBuffer>> _memberBuffers; + std::map<const char *, std::unique_ptr<Future<File *>>> _preloaded; + std::mutex _mutex; +}; + +class ArchiveReader : public Reader { +public: + ArchiveReader(bool logLoading) : _logLoading(logLoading) {} + + bool canParse(file_magic magic, StringRef, + const MemoryBuffer &) const override { + return (magic == llvm::sys::fs::file_magic::archive); + } + + std::error_code + loadFile(std::unique_ptr<MemoryBuffer> mb, const Registry ®, + std::vector<std::unique_ptr<File>> &result) const override { + StringRef path = mb->getBufferIdentifier(); + std::unique_ptr<FileArchive> file( + new FileArchive(std::move(mb), reg, path, _logLoading)); + result.push_back(std::move(file)); + return std::error_code(); + } + +private: + bool _logLoading; +}; + +} // anonymous namespace + +void Registry::addSupportArchives(bool logLoading) { + add(std::unique_ptr<Reader>(new ArchiveReader(logLoading))); +} + +} // end namespace lld diff --git a/lib/ReaderWriter/LinkerScript.cpp b/lib/ReaderWriter/LinkerScript.cpp new file mode 100644 index 0000000000000..56194cae5e724 --- /dev/null +++ b/lib/ReaderWriter/LinkerScript.cpp @@ -0,0 +1,2564 @@ +//===- ReaderWriter/LinkerScript.cpp --------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Linker script parser. +/// +//===----------------------------------------------------------------------===// + +#include "lld/ReaderWriter/LinkerScript.h" + +namespace lld { +namespace script { +void Token::dump(raw_ostream &os) const { + switch (_kind) { +#define CASE(name) \ + case Token::name: \ + os << #name ": "; \ + break; + CASE(unknown) + CASE(eof) + CASE(exclaim) + CASE(exclaimequal) + CASE(amp) + CASE(ampequal) + CASE(l_paren) + CASE(r_paren) + CASE(star) + CASE(starequal) + CASE(plus) + CASE(plusequal) + CASE(comma) + CASE(minus) + CASE(minusequal) + CASE(slash) + CASE(slashequal) + CASE(number) + CASE(colon) + CASE(semicolon) + CASE(less) + CASE(lessequal) + CASE(lessless) + CASE(lesslessequal) + CASE(equal) + CASE(equalequal) + CASE(greater) + CASE(greaterequal) + CASE(greatergreater) + CASE(greatergreaterequal) + CASE(question) + CASE(identifier) + CASE(libname) + CASE(kw_align) + CASE(kw_align_with_input) + CASE(kw_as_needed) + CASE(kw_at) + CASE(kw_discard) + CASE(kw_entry) + CASE(kw_exclude_file) + CASE(kw_extern) + CASE(kw_group) + CASE(kw_hidden) + CASE(kw_input) + CASE(kw_keep) + CASE(kw_length) + CASE(kw_memory) + CASE(kw_origin) + CASE(kw_provide) + CASE(kw_provide_hidden) + CASE(kw_only_if_ro) + CASE(kw_only_if_rw) + CASE(kw_output) + CASE(kw_output_arch) + CASE(kw_output_format) + CASE(kw_overlay) + CASE(kw_search_dir) + CASE(kw_sections) + CASE(kw_sort_by_alignment) + CASE(kw_sort_by_init_priority) + CASE(kw_sort_by_name) + CASE(kw_sort_none) + CASE(kw_subalign) + CASE(l_brace) + CASE(pipe) + CASE(pipeequal) + CASE(r_brace) + CASE(tilde) +#undef CASE + } + os << _range << "\n"; +} + +static llvm::ErrorOr<uint64_t> parseDecimal(StringRef str) { + uint64_t res = 0; + for (auto &c : str) { + res *= 10; + if (c < '0' || c > '9') + return llvm::ErrorOr<uint64_t>(std::make_error_code(std::errc::io_error)); + res += c - '0'; + } + return res; +} + +static llvm::ErrorOr<uint64_t> parseOctal(StringRef str) { + uint64_t res = 0; + for (auto &c : str) { + res <<= 3; + if (c < '0' || c > '7') + return llvm::ErrorOr<uint64_t>(std::make_error_code(std::errc::io_error)); + res += c - '0'; + } + return res; +} + +static llvm::ErrorOr<uint64_t> parseBinary(StringRef str) { + uint64_t res = 0; + for (auto &c : str) { + res <<= 1; + if (c != '0' && c != '1') + return llvm::ErrorOr<uint64_t>(std::make_error_code(std::errc::io_error)); + res += c - '0'; + } + return res; +} + +static llvm::ErrorOr<uint64_t> parseHex(StringRef str) { + uint64_t res = 0; + for (auto &c : str) { + res <<= 4; + if (c >= '0' && c <= '9') + res += c - '0'; + else if (c >= 'a' && c <= 'f') + res += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + res += c - 'A' + 10; + else + return llvm::ErrorOr<uint64_t>(std::make_error_code(std::errc::io_error)); + } + return res; +} + +static bool parseHexToByteStream(StringRef str, std::string &buf) { + unsigned char byte = 0; + bool dumpByte = str.size() % 2; + for (auto &c : str) { + byte <<= 4; + if (c >= '0' && c <= '9') + byte += c - '0'; + else if (c >= 'a' && c <= 'f') + byte += c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + byte += c - 'A' + 10; + else + return false; + if (!dumpByte) { + dumpByte = true; + continue; + } + buf.push_back(byte); + byte = 0; + dumpByte = false; + } + return !dumpByte; +} + +static void dumpByteStream(raw_ostream &os, StringRef stream) { + os << "0x"; + for (auto &c : stream) { + unsigned char firstNibble = c >> 4 & 0xF; + if (firstNibble > 9) + os << (char) ('A' + firstNibble - 10); + else + os << (char) ('0' + firstNibble); + unsigned char secondNibble = c & 0xF; + if (secondNibble > 9) + os << (char) ('A' + secondNibble - 10); + else + os << (char) ('0' + secondNibble); + } +} + +static llvm::ErrorOr<uint64_t> parseNum(StringRef str) { + unsigned multiplier = 1; + enum NumKind { decimal, hex, octal, binary }; + NumKind kind = llvm::StringSwitch<NumKind>(str) + .StartsWith("0x", hex) + .StartsWith("0X", hex) + .StartsWith("0", octal) + .Default(decimal); + + // Parse scale + if (str.endswith("K")) { + multiplier = 1 << 10; + str = str.drop_back(); + } else if (str.endswith("M")) { + multiplier = 1 << 20; + str = str.drop_back(); + } + + // Parse type + if (str.endswith_lower("o")) { + kind = octal; + str = str.drop_back(); + } else if (str.endswith_lower("h")) { + kind = hex; + str = str.drop_back(); + } else if (str.endswith_lower("d")) { + kind = decimal; + str = str.drop_back(); + } else if (str.endswith_lower("b")) { + kind = binary; + str = str.drop_back(); + } + + llvm::ErrorOr<uint64_t> res(0); + switch (kind) { + case hex: + if (str.startswith_lower("0x")) + str = str.drop_front(2); + res = parseHex(str); + break; + case octal: + res = parseOctal(str); + break; + case decimal: + res = parseDecimal(str); + break; + case binary: + res = parseBinary(str); + break; + } + if (res.getError()) + return res; + + *res = *res * multiplier; + return res; +} + +bool Lexer::canStartNumber(char c) const { + return '0' <= c && c <= '9'; +} + +bool Lexer::canContinueNumber(char c) const { + // [xX] = hex marker, [hHoO] = type suffix, [MK] = scale suffix. + return strchr("0123456789ABCDEFabcdefxXhHoOMK", c); +} + +bool Lexer::canStartName(char c) const { + return strchr( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_.$/\\*", c); +} + +bool Lexer::canContinueName(char c) const { + return strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789_.$/\\~=+[]*?-:", c); +} + +/// Helper function to split a StringRef in two at the nth character. +/// The StringRef s is updated, while the function returns the n first +/// characters. +static StringRef drop(StringRef &s, int n) { + StringRef res = s.substr(0, n); + s = s.drop_front(n); + return res; +} + +void Lexer::lex(Token &tok) { + skipWhitespace(); + if (_buffer.empty()) { + tok = Token(_buffer, Token::eof); + return; + } + switch (_buffer[0]) { + case 0: + tok = Token(drop(_buffer, 1), Token::eof); + return; + case '(': + tok = Token(drop(_buffer, 1), Token::l_paren); + return; + case ')': + tok = Token(drop(_buffer, 1), Token::r_paren); + return; + case '{': + tok = Token(drop(_buffer, 1), Token::l_brace); + return; + case '}': + tok = Token(drop(_buffer, 1), Token::r_brace); + return; + case '=': + if (_buffer.startswith("==")) { + tok = Token(drop(_buffer, 2), Token::equalequal); + return; + } + tok = Token(drop(_buffer, 1), Token::equal); + return; + case '!': + if (_buffer.startswith("!=")) { + tok = Token(drop(_buffer, 2), Token::exclaimequal); + return; + } + tok = Token(drop(_buffer, 1), Token::exclaim); + return; + case ',': + tok = Token(drop(_buffer, 1), Token::comma); + return; + case ';': + tok = Token(drop(_buffer, 1), Token::semicolon); + return; + case ':': + tok = Token(drop(_buffer, 1), Token::colon); + return; + case '&': + if (_buffer.startswith("&=")) { + tok = Token(drop(_buffer, 2), Token::ampequal); + return; + } + tok = Token(drop(_buffer, 1), Token::amp); + return; + case '|': + if (_buffer.startswith("|=")) { + tok = Token(drop(_buffer, 2), Token::pipeequal); + return; + } + tok = Token(drop(_buffer, 1), Token::pipe); + return; + case '+': + if (_buffer.startswith("+=")) { + tok = Token(drop(_buffer, 2), Token::plusequal); + return; + } + tok = Token(drop(_buffer, 1), Token::plus); + return; + case '-': { + if (_buffer.startswith("-=")) { + tok = Token(drop(_buffer, 2), Token::minusequal); + return; + } + if (!_buffer.startswith("-l")) { + tok = Token(drop(_buffer, 1), Token::minus); + return; + } + // -l<lib name> + _buffer = _buffer.drop_front(2); + StringRef::size_type start = 0; + if (_buffer[start] == ':') + ++start; + if (!canStartName(_buffer[start])) + // Create 'unknown' token. + break; + auto libNameEnd = std::find_if(_buffer.begin() + start + 1, _buffer.end(), + [=](char c) { return !canContinueName(c); }); + StringRef::size_type libNameLen = + std::distance(_buffer.begin(), libNameEnd); + tok = Token(_buffer.substr(0, libNameLen), Token::libname); + _buffer = _buffer.drop_front(libNameLen); + return; + } + case '<': + if (_buffer.startswith("<<=")) { + tok = Token(drop(_buffer, 3), Token::lesslessequal); + return; + } + if (_buffer.startswith("<<")) { + tok = Token(drop(_buffer, 2), Token::lessless); + return; + } + if (_buffer.startswith("<=")) { + tok = Token(drop(_buffer, 2), Token::lessequal); + return; + } + tok = Token(drop(_buffer, 1), Token::less); + return; + case '>': + if (_buffer.startswith(">>=")) { + tok = Token(drop(_buffer, 3), Token::greatergreaterequal); + return; + } + if (_buffer.startswith(">>")) { + tok = Token(drop(_buffer, 2), Token::greatergreater); + return; + } + if (_buffer.startswith(">=")) { + tok = Token(drop(_buffer, 2), Token::greaterequal); + return; + } + tok = Token(drop(_buffer, 1), Token::greater); + return; + case '~': + tok = Token(drop(_buffer, 1), Token::tilde); + return; + case '\"': case '\'': { + // Handle quoted strings. They are treated as identifiers for + // simplicity. + char c = _buffer[0]; + _buffer = _buffer.drop_front(); + auto quotedStringEnd = _buffer.find(c); + if (quotedStringEnd == StringRef::npos || quotedStringEnd == 0) + break; + StringRef word = _buffer.substr(0, quotedStringEnd); + tok = Token(word, Token::identifier); + _buffer = _buffer.drop_front(quotedStringEnd + 1); + return; + } + default: + // Handle literal numbers + if (canStartNumber(_buffer[0])) { + auto endIter = std::find_if(_buffer.begin(), _buffer.end(), [=](char c) { + return !canContinueNumber(c); + }); + StringRef::size_type end = endIter == _buffer.end() + ? StringRef::npos + : std::distance(_buffer.begin(), endIter); + if (end == StringRef::npos || end == 0) + break; + StringRef word = _buffer.substr(0, end); + tok = Token(word, Token::number); + _buffer = _buffer.drop_front(end); + return; + } + // Handle slashes '/', which can be either an operator inside an expression + // or the beginning of an identifier + if (_buffer.startswith("/=")) { + tok = Token(drop(_buffer, 2), Token::slashequal); + return; + } + if (_buffer[0] == '/' && _buffer.size() > 1 && + !canContinueName(_buffer[1])) { + tok = Token(drop(_buffer, 1), Token::slash); + return; + } + // Handle stars '*' + if (_buffer.startswith("*=")) { + tok = Token(drop(_buffer, 2), Token::starequal); + return; + } + if (_buffer[0] == '*' && _buffer.size() > 1 && + !canContinueName(_buffer[1])) { + tok = Token(drop(_buffer, 1), Token::star); + return; + } + // Handle questions '?' + if (_buffer[0] == '?' && _buffer.size() > 1 && + !canContinueName(_buffer[1])) { + tok = Token(drop(_buffer, 1), Token::question); + return; + } + // keyword or identifier. + if (!canStartName(_buffer[0])) + break; + auto endIter = std::find_if(_buffer.begin() + 1, _buffer.end(), + [=](char c) { return !canContinueName(c); }); + StringRef::size_type end = endIter == _buffer.end() + ? StringRef::npos + : std::distance(_buffer.begin(), endIter); + if (end == StringRef::npos || end == 0) + break; + StringRef word = _buffer.substr(0, end); + Token::Kind kind = + llvm::StringSwitch<Token::Kind>(word) + .Case("ALIGN", Token::kw_align) + .Case("ALIGN_WITH_INPUT", Token::kw_align_with_input) + .Case("AS_NEEDED", Token::kw_as_needed) + .Case("AT", Token::kw_at) + .Case("ENTRY", Token::kw_entry) + .Case("EXCLUDE_FILE", Token::kw_exclude_file) + .Case("EXTERN", Token::kw_extern) + .Case("GROUP", Token::kw_group) + .Case("HIDDEN", Token::kw_hidden) + .Case("INPUT", Token::kw_input) + .Case("KEEP", Token::kw_keep) + .Case("LENGTH", Token::kw_length) + .Case("l", Token::kw_length) + .Case("len", Token::kw_length) + .Case("MEMORY", Token::kw_memory) + .Case("ONLY_IF_RO", Token::kw_only_if_ro) + .Case("ONLY_IF_RW", Token::kw_only_if_rw) + .Case("ORIGIN", Token::kw_origin) + .Case("o", Token::kw_origin) + .Case("org", Token::kw_origin) + .Case("OUTPUT", Token::kw_output) + .Case("OUTPUT_ARCH", Token::kw_output_arch) + .Case("OUTPUT_FORMAT", Token::kw_output_format) + .Case("OVERLAY", Token::kw_overlay) + .Case("PROVIDE", Token::kw_provide) + .Case("PROVIDE_HIDDEN", Token::kw_provide_hidden) + .Case("SEARCH_DIR", Token::kw_search_dir) + .Case("SECTIONS", Token::kw_sections) + .Case("SORT", Token::kw_sort_by_name) + .Case("SORT_BY_ALIGNMENT", Token::kw_sort_by_alignment) + .Case("SORT_BY_INIT_PRIORITY", Token::kw_sort_by_init_priority) + .Case("SORT_BY_NAME", Token::kw_sort_by_name) + .Case("SORT_NONE", Token::kw_sort_none) + .Case("SUBALIGN", Token::kw_subalign) + .Case("/DISCARD/", Token::kw_discard) + .Default(Token::identifier); + tok = Token(word, kind); + _buffer = _buffer.drop_front(end); + return; + } + tok = Token(drop(_buffer, 1), Token::unknown); +} + +void Lexer::skipWhitespace() { + while (true) { + if (_buffer.empty()) + return; + switch (_buffer[0]) { + case ' ': + case '\r': + case '\n': + case '\t': + _buffer = _buffer.drop_front(); + break; + // Potential comment. + case '/': + if (_buffer.size() <= 1 || _buffer[1] != '*') + return; + // Skip starting /* + _buffer = _buffer.drop_front(2); + // If the next char is also a /, it's not the end. + if (!_buffer.empty() && _buffer[0] == '/') + _buffer = _buffer.drop_front(); + + // Scan for /'s. We're done if it is preceded by a *. + while (true) { + if (_buffer.empty()) + break; + _buffer = _buffer.drop_front(); + if (_buffer.data()[-1] == '/' && _buffer.data()[-2] == '*') + break; + } + break; + default: + return; + } + } +} + +// Constant functions +void Constant::dump(raw_ostream &os) const { os << _num; } + +ErrorOr<int64_t> Constant::evalExpr(SymbolTableTy &symbolTable) const { + return _num; +} + +// Symbol functions +void Symbol::dump(raw_ostream &os) const { os << _name; } + +ErrorOr<int64_t> Symbol::evalExpr(SymbolTableTy &symbolTable) const { + auto it = symbolTable.find(_name); + if (it == symbolTable.end()) + return LinkerScriptReaderError::unknown_symbol_in_expr; + return it->second; +} + +// FunctionCall functions +void FunctionCall::dump(raw_ostream &os) const { + os << _name << "("; + for (unsigned i = 0, e = _args.size(); i != e; ++i) { + if (i) + os << ", "; + _args[i]->dump(os); + } + os << ")"; +} + +ErrorOr<int64_t> FunctionCall::evalExpr(SymbolTableTy &symbolTable) const { + return LinkerScriptReaderError::unrecognized_function_in_expr; +} + +// Unary functions +void Unary::dump(raw_ostream &os) const { + os << "("; + if (_op == Unary::Minus) + os << "-"; + else + os << "~"; + _child->dump(os); + os << ")"; +} + +ErrorOr<int64_t> Unary::evalExpr(SymbolTableTy &symbolTable) const { + auto child = _child->evalExpr(symbolTable); + if (child.getError()) + return child.getError(); + + int64_t childRes = *child; + switch (_op) { + case Unary::Minus: + return -childRes; + case Unary::Not: + return ~childRes; + } + + llvm_unreachable(""); +} + +// BinOp functions +void BinOp::dump(raw_ostream &os) const { + os << "("; + _lhs->dump(os); + os << " "; + switch (_op) { + case Sum: + os << "+"; + break; + case Sub: + os << "-"; + break; + case Mul: + os << "*"; + break; + case Div: + os << "/"; + break; + case Shl: + os << "<<"; + break; + case Shr: + os << ">>"; + break; + case And: + os << "&"; + break; + case Or: + os << "|"; + break; + case CompareEqual: + os << "=="; + break; + case CompareDifferent: + os << "!="; + break; + case CompareLess: + os << "<"; + break; + case CompareGreater: + os << ">"; + break; + case CompareLessEqual: + os << "<="; + break; + case CompareGreaterEqual: + os << ">="; + break; + } + os << " "; + _rhs->dump(os); + os << ")"; +} + +ErrorOr<int64_t> BinOp::evalExpr(SymbolTableTy &symbolTable) const { + auto lhs = _lhs->evalExpr(symbolTable); + if (lhs.getError()) + return lhs.getError(); + auto rhs = _rhs->evalExpr(symbolTable); + if (rhs.getError()) + return rhs.getError(); + + int64_t lhsRes = *lhs; + int64_t rhsRes = *rhs; + + switch(_op) { + case And: return lhsRes & rhsRes; + case CompareDifferent: return lhsRes != rhsRes; + case CompareEqual: return lhsRes == rhsRes; + case CompareGreater: return lhsRes > rhsRes; + case CompareGreaterEqual: return lhsRes >= rhsRes; + case CompareLess: return lhsRes < rhsRes; + case CompareLessEqual: return lhsRes <= rhsRes; + case Div: return lhsRes / rhsRes; + case Mul: return lhsRes * rhsRes; + case Or: return lhsRes | rhsRes; + case Shl: return lhsRes << rhsRes; + case Shr: return lhsRes >> rhsRes; + case Sub: return lhsRes - rhsRes; + case Sum: return lhsRes + rhsRes; + } + + llvm_unreachable(""); +} + +// TernaryConditional functions +void TernaryConditional::dump(raw_ostream &os) const { + _conditional->dump(os); + os << " ? "; + _trueExpr->dump(os); + os << " : "; + _falseExpr->dump(os); +} + +ErrorOr<int64_t> +TernaryConditional::evalExpr(SymbolTableTy &symbolTable) const { + auto conditional = _conditional->evalExpr(symbolTable); + if (conditional.getError()) + return conditional.getError(); + if (*conditional) + return _trueExpr->evalExpr(symbolTable); + return _falseExpr->evalExpr(symbolTable); +} + +// SymbolAssignment functions +void SymbolAssignment::dump(raw_ostream &os) const { + int numParen = 0; + + if (_assignmentVisibility != Default) { + switch (_assignmentVisibility) { + case Hidden: + os << "HIDDEN("; + break; + case Provide: + os << "PROVIDE("; + break; + case ProvideHidden: + os << "PROVIDE_HIDDEN("; + break; + default: + llvm_unreachable("Unknown visibility"); + } + ++numParen; + } + + os << _symbol << " "; + switch (_assignmentKind) { + case Simple: + os << "="; + break; + case Sum: + os << "+="; + break; + case Sub: + os << "-="; + break; + case Mul: + os << "*="; + break; + case Div: + os << "/="; + break; + case Shl: + os << "<<="; + break; + case Shr: + os << ">>="; + break; + case And: + os << "&="; + break; + case Or: + os << "|="; + break; + } + + os << " "; + _expression->dump(os); + if (numParen) + os << ")"; + os << ";"; +} + +static int dumpSortDirectives(raw_ostream &os, WildcardSortMode sortMode) { + switch (sortMode) { + case WildcardSortMode::NA: + return 0; + case WildcardSortMode::ByName: + os << "SORT_BY_NAME("; + return 1; + case WildcardSortMode::ByAlignment: + os << "SORT_BY_ALIGNMENT("; + return 1; + case WildcardSortMode::ByInitPriority: + os << "SORT_BY_INIT_PRIORITY("; + return 1; + case WildcardSortMode::ByNameAndAlignment: + os << "SORT_BY_NAME(SORT_BY_ALIGNMENT("; + return 2; + case WildcardSortMode::ByAlignmentAndName: + os << "SORT_BY_ALIGNMENT(SORT_BY_NAME("; + return 2; + case WildcardSortMode::None: + os << "SORT_NONE("; + return 1; + } + return 0; +} + +// InputSectionName functions +void InputSectionName::dump(raw_ostream &os) const { + os << _name; +} + +// InputSectionSortedGroup functions +static void dumpInputSections(raw_ostream &os, + llvm::ArrayRef<const InputSection *> secs) { + bool excludeFile = false; + bool first = true; + + for (auto &secName : secs) { + if (!first) + os << " "; + first = false; + // Coalesce multiple input sections marked with EXCLUDE_FILE in the same + // EXCLUDE_FILE() group + if (auto inputSec = dyn_cast<InputSectionName>(secName)) { + if (!excludeFile && inputSec->hasExcludeFile()) { + excludeFile = true; + os << "EXCLUDE_FILE("; + } else if (excludeFile && !inputSec->hasExcludeFile()) { + excludeFile = false; + os << ") "; + } + } + secName->dump(os); + } + + if (excludeFile) + os << ")"; +} + +void InputSectionSortedGroup::dump(raw_ostream &os) const { + int numParen = dumpSortDirectives(os, _sortMode); + dumpInputSections(os, _sections); + for (int i = 0; i < numParen; ++i) + os << ")"; +} + +// InputSectionsCmd functions +void InputSectionsCmd::dump(raw_ostream &os) const { + if (_keep) + os << "KEEP("; + + int numParen = dumpSortDirectives(os, _fileSortMode); + os << _memberName; + for (int i = 0; i < numParen; ++i) + os << ")"; + + if (_archiveName.size() > 0) { + os << ":"; + numParen = dumpSortDirectives(os, _archiveSortMode); + os << _archiveName; + for (int i = 0; i < numParen; ++i) + os << ")"; + } + + if (_sections.size() > 0) { + os << "("; + dumpInputSections(os, _sections); + os << ")"; + } + + if (_keep) + os << ")"; +} + +// OutputSectionDescription functions +void OutputSectionDescription::dump(raw_ostream &os) const { + if (_discard) + os << "/DISCARD/"; + else + os << _sectionName; + + if (_address) { + os << " "; + _address->dump(os); + } + os << " :\n"; + + if (_at) { + os << " AT("; + _at->dump(os); + os << ")\n"; + } + + if (_align) { + os << " ALIGN("; + _align->dump(os); + os << ")\n"; + } else if (_alignWithInput) { + os << " ALIGN_WITH_INPUT\n"; + } + + if (_subAlign) { + os << " SUBALIGN("; + _subAlign->dump(os); + os << ")\n"; + } + + switch (_constraint) { + case C_None: + break; + case C_OnlyIfRO: + os << "ONLY_IF_RO"; + break; + case C_OnlyIfRW: + os << "ONLY_IF_RW"; + break; + } + + os << " {\n"; + for (auto &command : _outputSectionCommands) { + os << " "; + command->dump(os); + os << "\n"; + } + os << " }"; + + if (_fillStream.size() > 0) { + os << " ="; + dumpByteStream(os, _fillStream); + } else if (_fillExpr) { + os << " ="; + _fillExpr->dump(os); + } +} + +// Sections functions +void Sections::dump(raw_ostream &os) const { + os << "SECTIONS\n{\n"; + for (auto &command : _sectionsCommands) { + command->dump(os); + os << "\n"; + } + os << "}\n"; +} + +// Memory functions +void MemoryBlock::dump(raw_ostream &os) const { + os << _name; + + if (!_attr.empty()) + os << " (" << _attr << ")"; + + os << " : "; + + os << "ORIGIN = "; + _origin->dump(os); + os << ", "; + + os << "LENGTH = "; + _length->dump(os); +} + +void Memory::dump(raw_ostream &os) const { + os << "MEMORY\n{\n"; + for (auto &block : _blocks) { + block->dump(os); + os << "\n"; + } + os << "}\n"; +} + +// Extern functions +void Extern::dump(raw_ostream &os) const { + os << "EXTERN("; + for (unsigned i = 0, e = _symbols.size(); i != e; ++i) { + if (i) + os << " "; + os << _symbols[i]; + } + os << ")\n"; +} + + +// Parser functions +std::error_code Parser::parse() { + // Get the first token. + _lex.lex(_tok); + // Parse top level commands. + while (true) { + switch (_tok._kind) { + case Token::eof: + return std::error_code(); + case Token::semicolon: + consumeToken(); + break; + case Token::kw_output: { + auto output = parseOutput(); + if (!output) + return LinkerScriptReaderError::parse_error; + _script._commands.push_back(output); + break; + } + case Token::kw_output_format: { + auto outputFormat = parseOutputFormat(); + if (!outputFormat) + return LinkerScriptReaderError::parse_error; + _script._commands.push_back(outputFormat); + break; + } + case Token::kw_output_arch: { + auto outputArch = parseOutputArch(); + if (!outputArch) + return LinkerScriptReaderError::parse_error; + _script._commands.push_back(outputArch); + break; + } + case Token::kw_input: { + Input *input = parsePathList<Input>(); + if (!input) + return LinkerScriptReaderError::parse_error; + _script._commands.push_back(input); + break; + } + case Token::kw_group: { + Group *group = parsePathList<Group>(); + if (!group) + return LinkerScriptReaderError::parse_error; + _script._commands.push_back(group); + break; + } + case Token::kw_as_needed: + // Not allowed at top level. + error(_tok, "AS_NEEDED not allowed at top level."); + return LinkerScriptReaderError::parse_error; + case Token::kw_entry: { + Entry *entry = parseEntry(); + if (!entry) + return LinkerScriptReaderError::parse_error; + _script._commands.push_back(entry); + break; + } + case Token::kw_search_dir: { + SearchDir *searchDir = parseSearchDir(); + if (!searchDir) + return LinkerScriptReaderError::parse_error; + _script._commands.push_back(searchDir); + break; + } + case Token::kw_sections: { + Sections *sections = parseSections(); + if (!sections) + return LinkerScriptReaderError::parse_error; + _script._commands.push_back(sections); + break; + } + case Token::identifier: + case Token::kw_hidden: + case Token::kw_provide: + case Token::kw_provide_hidden: { + const Command *cmd = parseSymbolAssignment(); + if (!cmd) + return LinkerScriptReaderError::parse_error; + _script._commands.push_back(cmd); + break; + } + case Token::kw_memory: { + const Command *cmd = parseMemory(); + if (!cmd) + return LinkerScriptReaderError::parse_error; + _script._commands.push_back(cmd); + break; + } + case Token::kw_extern: { + const Command *cmd = parseExtern(); + if (!cmd) + return LinkerScriptReaderError::parse_error; + _script._commands.push_back(cmd); + break; + } + default: + // Unexpected. + error(_tok, "expected linker script command"); + return LinkerScriptReaderError::parse_error; + } + } + return LinkerScriptReaderError::parse_error; +} + +const Expression *Parser::parseFunctionCall() { + assert((_tok._kind == Token::identifier || _tok._kind == Token::kw_align) && + "expected function call first tokens"); + SmallVector<const Expression *, 8> params; + StringRef name = _tok._range; + + consumeToken(); + if (!expectAndConsume(Token::l_paren, "expected (")) + return nullptr; + + if (_tok._kind == Token::r_paren) { + consumeToken(); + return new (_alloc) FunctionCall(*this, _tok._range, params); + } + + if (const Expression *firstParam = parseExpression()) + params.push_back(firstParam); + else + return nullptr; + + while (_tok._kind == Token::comma) { + consumeToken(); + if (const Expression *param = parseExpression()) + params.push_back(param); + else + return nullptr; + } + + if (!expectAndConsume(Token::r_paren, "expected )")) + return nullptr; + return new (_alloc) FunctionCall(*this, name, params); +} + +bool Parser::expectExprOperand() { + if (!(_tok._kind == Token::identifier || _tok._kind == Token::number || + _tok._kind == Token::kw_align || _tok._kind == Token::l_paren || + _tok._kind == Token::minus || _tok._kind == Token::tilde)) { + error(_tok, "expected symbol, number, minus, tilde or left parenthesis."); + return false; + } + return true; +} + +const Expression *Parser::parseExprOperand() { + if (!expectExprOperand()) + return nullptr; + + switch (_tok._kind) { + case Token::identifier: { + if (peek()._kind== Token::l_paren) + return parseFunctionCall(); + Symbol *sym = new (_alloc) Symbol(*this, _tok._range); + consumeToken(); + return sym; + } + case Token::kw_align: + return parseFunctionCall(); + case Token::minus: + consumeToken(); + return new (_alloc) Unary(*this, Unary::Minus, parseExprOperand()); + case Token::tilde: + consumeToken(); + return new (_alloc) Unary(*this, Unary::Not, parseExprOperand()); + case Token::number: { + auto val = parseNum(_tok._range); + if (val.getError()) { + error(_tok, "Unrecognized number constant"); + return nullptr; + } + Constant *c = new (_alloc) Constant(*this, *val); + consumeToken(); + return c; + } + case Token::l_paren: { + consumeToken(); + const Expression *expr = parseExpression(); + if (!expectAndConsume(Token::r_paren, "expected )")) + return nullptr; + return expr; + } + default: + llvm_unreachable("Unknown token"); + } +} + +static bool TokenToBinOp(const Token &tok, BinOp::Operation &op, + unsigned &precedence) { + switch (tok._kind) { + case Token::star: + op = BinOp::Mul; + precedence = 3; + return true; + case Token::slash: + op = BinOp::Div; + precedence = 3; + return true; + case Token::plus: + op = BinOp::Sum; + precedence = 4; + return true; + case Token::minus: + op = BinOp::Sub; + precedence = 4; + return true; + case Token::lessless: + op = BinOp::Shl; + precedence = 5; + return true; + case Token::greatergreater: + op = BinOp::Shr; + precedence = 5; + return true; + case Token::less: + op = BinOp::CompareLess; + precedence = 6; + return true; + case Token::greater: + op = BinOp::CompareGreater; + precedence = 6; + return true; + case Token::lessequal: + op = BinOp::CompareLessEqual; + precedence = 6; + return true; + case Token::greaterequal: + op = BinOp::CompareGreaterEqual; + precedence = 6; + return true; + case Token::equalequal: + op = BinOp::CompareEqual; + precedence = 7; + return true; + case Token::exclaimequal: + op = BinOp::CompareDifferent; + precedence = 7; + return true; + case Token::amp: + op = BinOp::And; + precedence = 8; + return true; + case Token::pipe: + op = BinOp::Or; + precedence = 10; + return true; + default: + break; + } + return false; +} + +static bool isExpressionOperator(Token tok) { + switch (tok._kind) { + case Token::star: + case Token::slash: + case Token::plus: + case Token::minus: + case Token::lessless: + case Token::greatergreater: + case Token::less: + case Token::greater: + case Token::lessequal: + case Token::greaterequal: + case Token::equalequal: + case Token::exclaimequal: + case Token::amp: + case Token::pipe: + case Token::question: + return true; + default: + return false; + } +} + +const Expression *Parser::parseExpression(unsigned precedence) { + assert(precedence <= 13 && "Invalid precedence value"); + if (!expectExprOperand()) + return nullptr; + + const Expression *expr = parseExprOperand(); + if (!expr) + return nullptr; + + BinOp::Operation op; + unsigned binOpPrecedence = 0; + if (TokenToBinOp(_tok, op, binOpPrecedence)) { + if (precedence >= binOpPrecedence) + return parseOperatorOperandLoop(expr, precedence); + return expr; + } + + // Non-binary operators + if (_tok._kind == Token::question && precedence >= 13) + return parseOperatorOperandLoop(expr, precedence); + return expr; +} + +const Expression *Parser::parseOperatorOperandLoop(const Expression *lhs, + unsigned highestPrecedence) { + assert(highestPrecedence <= 13 && "Invalid precedence value"); + unsigned precedence = 0; + const Expression *binOp = nullptr; + + while (1) { + BinOp::Operation op; + if (!TokenToBinOp(_tok, op, precedence)) { + if (_tok._kind == Token::question && highestPrecedence >= 13) + return parseTernaryCondOp(lhs); + return binOp; + } + + if (precedence > highestPrecedence) + return binOp; + + consumeToken(); + const Expression *rhs = parseExpression(precedence - 1); + if (!rhs) + return nullptr; + binOp = new (_alloc) BinOp(*this, lhs, op, rhs); + lhs = binOp; + } +} + +const Expression *Parser::parseTernaryCondOp(const Expression *lhs) { + assert(_tok._kind == Token::question && "Expected question mark"); + + consumeToken(); + + // The ternary conditional operator has right-to-left associativity. + // To implement this, we allow our children to contain ternary conditional + // operators themselves (precedence 13). + const Expression *trueExpr = parseExpression(13); + if (!trueExpr) + return nullptr; + + if (!expectAndConsume(Token::colon, "expected :")) + return nullptr; + + const Expression *falseExpr = parseExpression(13); + if (!falseExpr) + return nullptr; + + return new (_alloc) TernaryConditional(*this, lhs, trueExpr, falseExpr); +} + +// Parse OUTPUT(ident) +Output *Parser::parseOutput() { + assert(_tok._kind == Token::kw_output && "Expected OUTPUT"); + consumeToken(); + if (!expectAndConsume(Token::l_paren, "expected (")) + return nullptr; + + if (_tok._kind != Token::identifier) { + error(_tok, "Expected identifier in OUTPUT."); + return nullptr; + } + + auto ret = new (_alloc) Output(*this, _tok._range); + consumeToken(); + + if (!expectAndConsume(Token::r_paren, "expected )")) + return nullptr; + + return ret; +} + +// Parse OUTPUT_FORMAT(ident) +OutputFormat *Parser::parseOutputFormat() { + assert(_tok._kind == Token::kw_output_format && "Expected OUTPUT_FORMAT!"); + consumeToken(); + if (!expectAndConsume(Token::l_paren, "expected (")) + return nullptr; + + if (_tok._kind != Token::identifier) { + error(_tok, "Expected identifier in OUTPUT_FORMAT."); + return nullptr; + } + + SmallVector<StringRef, 8> formats; + formats.push_back(_tok._range); + + consumeToken(); + + do { + if (isNextToken(Token::comma)) + consumeToken(); + else + break; + if (_tok._kind != Token::identifier) { + error(_tok, "Expected identifier in OUTPUT_FORMAT."); + return nullptr; + } + formats.push_back(_tok._range); + consumeToken(); + } while (isNextToken(Token::comma)); + + if (!expectAndConsume(Token::r_paren, "expected )")) + return nullptr; + + return new (_alloc) OutputFormat(*this, formats); +} + +// Parse OUTPUT_ARCH(ident) +OutputArch *Parser::parseOutputArch() { + assert(_tok._kind == Token::kw_output_arch && "Expected OUTPUT_ARCH!"); + consumeToken(); + if (!expectAndConsume(Token::l_paren, "expected (")) + return nullptr; + + if (_tok._kind != Token::identifier) { + error(_tok, "Expected identifier in OUTPUT_ARCH."); + return nullptr; + } + + auto ret = new (_alloc) OutputArch(*this, _tok._range); + consumeToken(); + + if (!expectAndConsume(Token::r_paren, "expected )")) + return nullptr; + + return ret; +} + +// Parse file list for INPUT or GROUP +template<class T> T *Parser::parsePathList() { + consumeToken(); + if (!expectAndConsume(Token::l_paren, "expected (")) + return nullptr; + + SmallVector<Path, 8> paths; + while (_tok._kind == Token::identifier || _tok._kind == Token::libname || + _tok._kind == Token::kw_as_needed) { + switch (_tok._kind) { + case Token::identifier: + paths.push_back(Path(_tok._range)); + consumeToken(); + break; + case Token::libname: + paths.push_back(Path(_tok._range, false, true)); + consumeToken(); + break; + case Token::kw_as_needed: + if (!parseAsNeeded(paths)) + return nullptr; + break; + default: + llvm_unreachable("Invalid token."); + } + } + if (!expectAndConsume(Token::r_paren, "expected )")) + return nullptr; + return new (_alloc) T(*this, paths); +} + +// Parse AS_NEEDED(file ...) +bool Parser::parseAsNeeded(SmallVectorImpl<Path> &paths) { + assert(_tok._kind == Token::kw_as_needed && "Expected AS_NEEDED!"); + consumeToken(); + if (!expectAndConsume(Token::l_paren, "expected (")) + return false; + + while (_tok._kind == Token::identifier || _tok._kind == Token::libname) { + switch (_tok._kind) { + case Token::identifier: + paths.push_back(Path(_tok._range, true, false)); + consumeToken(); + break; + case Token::libname: + paths.push_back(Path(_tok._range, true, true)); + consumeToken(); + break; + default: + llvm_unreachable("Invalid token."); + } + } + + if (!expectAndConsume(Token::r_paren, "expected )")) + return false; + return true; +} + +// Parse ENTRY(ident) +Entry *Parser::parseEntry() { + assert(_tok._kind == Token::kw_entry && "Expected ENTRY!"); + consumeToken(); + if (!expectAndConsume(Token::l_paren, "expected (")) + return nullptr; + if (_tok._kind != Token::identifier) { + error(_tok, "expected identifier in ENTRY"); + return nullptr; + } + StringRef entryName(_tok._range); + consumeToken(); + if (!expectAndConsume(Token::r_paren, "expected )")) + return nullptr; + return new (_alloc) Entry(*this, entryName); +} + +// Parse SEARCH_DIR(ident) +SearchDir *Parser::parseSearchDir() { + assert(_tok._kind == Token::kw_search_dir && "Expected SEARCH_DIR!"); + consumeToken(); + if (!expectAndConsume(Token::l_paren, "expected (")) + return nullptr; + if (_tok._kind != Token::identifier) { + error(_tok, "expected identifier in SEARCH_DIR"); + return nullptr; + } + StringRef searchPath(_tok._range); + consumeToken(); + if (!expectAndConsume(Token::r_paren, "expected )")) + return nullptr; + return new (_alloc) SearchDir(*this, searchPath); +} + +const SymbolAssignment *Parser::parseSymbolAssignment() { + assert((_tok._kind == Token::identifier || _tok._kind == Token::kw_hidden || + _tok._kind == Token::kw_provide || + _tok._kind == Token::kw_provide_hidden) && + "Expected identifier!"); + SymbolAssignment::AssignmentVisibility visibility = SymbolAssignment::Default; + SymbolAssignment::AssignmentKind kind; + int numParen = 0; + + switch (_tok._kind) { + case Token::kw_hidden: + visibility = SymbolAssignment::Hidden; + ++numParen; + consumeToken(); + if (!expectAndConsume(Token::l_paren, "expected (")) + return nullptr; + break; + case Token::kw_provide: + visibility = SymbolAssignment::Provide; + ++numParen; + consumeToken(); + if (!expectAndConsume(Token::l_paren, "expected (")) + return nullptr; + break; + case Token::kw_provide_hidden: + visibility = SymbolAssignment::ProvideHidden; + ++numParen; + consumeToken(); + if (!expectAndConsume(Token::l_paren, "expected (")) + return nullptr; + break; + default: + break; + } + + StringRef name = _tok._range; + consumeToken(); + + // Parse assignment operator (=, +=, -= etc.) + switch (_tok._kind) { + case Token::equal: + kind = SymbolAssignment::Simple; + break; + case Token::plusequal: + kind = SymbolAssignment::Sum; + break; + case Token::minusequal: + kind = SymbolAssignment::Sub; + break; + case Token::starequal: + kind = SymbolAssignment::Mul; + break; + case Token::slashequal: + kind = SymbolAssignment::Div; + break; + case Token::ampequal: + kind = SymbolAssignment::And; + break; + case Token::pipeequal: + kind = SymbolAssignment::Or; + break; + case Token::lesslessequal: + kind = SymbolAssignment::Shl; + break; + case Token::greatergreaterequal: + kind = SymbolAssignment::Shr; + break; + default: + error(_tok, "unexpected token"); + return nullptr; + } + + consumeToken(); + + const Expression *expr = nullptr; + switch (_tok._kind) { + case Token::number: + case Token::kw_align: + case Token::identifier: + case Token::l_paren: + expr = parseExpression(); + if (!expr) + return nullptr; + break; + default: + error(_tok, "unexpected token while parsing assignment value."); + return nullptr; + } + + for (int i = 0; i < numParen; ++i) + if (!expectAndConsume(Token::r_paren, "expected )")) + return nullptr; + + return new (_alloc) SymbolAssignment(*this, name, expr, kind, visibility); +} + +llvm::ErrorOr<InputSectionsCmd::VectorTy> Parser::parseExcludeFile() { + assert(_tok._kind == Token::kw_exclude_file && "Expected EXCLUDE_FILE!"); + InputSectionsCmd::VectorTy res; + consumeToken(); + + if (!expectAndConsume(Token::l_paren, "expected (")) + return llvm::ErrorOr<InputSectionsCmd::VectorTy>( + std::make_error_code(std::errc::io_error)); + + while (_tok._kind == Token::identifier) { + res.push_back(new (_alloc) InputSectionName(*this, _tok._range, true)); + consumeToken(); + } + + if (!expectAndConsume(Token::r_paren, "expected )")) + return llvm::ErrorOr<InputSectionsCmd::VectorTy>( + std::make_error_code(std::errc::io_error)); + return llvm::ErrorOr<InputSectionsCmd::VectorTy>(std::move(res)); +} + +int Parser::parseSortDirectives(WildcardSortMode &sortMode) { + int numParsedDirectives = 0; + sortMode = WildcardSortMode::NA; + + if (_tok._kind == Token::kw_sort_by_name) { + consumeToken(); + if (!expectAndConsume(Token::l_paren, "expected (")) + return -1; + ++numParsedDirectives; + sortMode = WildcardSortMode::ByName; + } + + if (_tok._kind == Token::kw_sort_by_init_priority) { + consumeToken(); + if (!expectAndConsume(Token::l_paren, "expected (")) + return -1; + ++numParsedDirectives; + sortMode = WildcardSortMode::ByInitPriority; + } + + if (_tok._kind == Token::kw_sort_by_alignment) { + consumeToken(); + if (!expectAndConsume(Token::l_paren, "expected (")) + return -1; + ++numParsedDirectives; + if (sortMode != WildcardSortMode::ByName) + sortMode = WildcardSortMode::ByAlignment; + else + sortMode = WildcardSortMode::ByNameAndAlignment; + } + + if (numParsedDirectives < 2 && _tok._kind == Token::kw_sort_by_name) { + consumeToken(); + if (!expectAndConsume(Token::l_paren, "expected (")) + return -1; + ++numParsedDirectives; + if (sortMode == WildcardSortMode::ByAlignment) + sortMode = WildcardSortMode::ByAlignmentAndName; + } + + if (numParsedDirectives < 2 && _tok._kind == Token::kw_sort_by_alignment) { + consumeToken(); + if (!expectAndConsume(Token::l_paren, "expected (")) + return -1; + ++numParsedDirectives; + } + + if (numParsedDirectives == 0 && _tok._kind == Token::kw_sort_none) { + consumeToken(); + if (!expectAndConsume(Token::l_paren, "expected (")) + return -1; + ++numParsedDirectives; + sortMode = WildcardSortMode::None; + } + + return numParsedDirectives; +} + +const InputSection *Parser::parseSortedInputSections() { + assert((_tok._kind == Token::kw_sort_by_name || + _tok._kind == Token::kw_sort_by_alignment || + _tok._kind == Token::kw_sort_by_init_priority || + _tok._kind == Token::kw_sort_none) && + "Expected SORT directives!"); + + WildcardSortMode sortMode = WildcardSortMode::NA; + int numParen = parseSortDirectives(sortMode); + if (numParen == -1) + return nullptr; + + SmallVector<const InputSection *, 8> inputSections; + + while (_tok._kind == Token::identifier) { + inputSections.push_back(new (_alloc) + InputSectionName(*this, _tok._range, false)); + consumeToken(); + } + + // Eat "numParen" rparens + for (int i = 0, e = numParen; i != e; ++i) + if (!expectAndConsume(Token::r_paren, "expected )")) + return nullptr; + + return new (_alloc) InputSectionSortedGroup(*this, sortMode, inputSections); +} + +const InputSectionsCmd *Parser::parseInputSectionsCmd() { + assert((_tok._kind == Token::identifier || _tok._kind == Token::colon || + _tok._kind == Token::star || _tok._kind == Token::kw_keep || + _tok._kind == Token::kw_sort_by_name || + _tok._kind == Token::kw_sort_by_alignment || + _tok._kind == Token::kw_sort_by_init_priority || + _tok._kind == Token::kw_sort_none) && + "Expected input section first tokens!"); + int numParen = 1; + bool keep = false; + WildcardSortMode fileSortMode = WildcardSortMode::NA; + WildcardSortMode archiveSortMode = WildcardSortMode::NA; + StringRef memberName; + StringRef archiveName; + + if (_tok._kind == Token::kw_keep) { + consumeToken(); + if (!expectAndConsume(Token::l_paren, "expected (")) + return nullptr; + ++numParen; + keep = true; + } + + // Input name + if (_tok._kind != Token::colon) { + int numParen = parseSortDirectives(fileSortMode); + if (numParen == -1) + return nullptr; + memberName = _tok._range; + consumeToken(); + if (numParen) { + while (numParen--) + if (!expectAndConsume(Token::r_paren, "expected )")) + return nullptr; + } + } + if (_tok._kind == Token::colon) { + consumeToken(); + if (_tok._kind == Token::identifier || + _tok._kind == Token::kw_sort_by_name || + _tok._kind == Token::kw_sort_by_alignment || + _tok._kind == Token::kw_sort_by_init_priority || + _tok._kind == Token::kw_sort_none) { + int numParen = parseSortDirectives(archiveSortMode); + if (numParen == -1) + return nullptr; + archiveName = _tok._range; + consumeToken(); + for (int i = 0; i != numParen; ++i) + if (!expectAndConsume(Token::r_paren, "expected )")) + return nullptr; + } + } + + SmallVector<const InputSection *, 8> inputSections; + + if (_tok._kind != Token::l_paren) + return new (_alloc) + InputSectionsCmd(*this, memberName, archiveName, keep, fileSortMode, + archiveSortMode, inputSections); + consumeToken(); + + while (_tok._kind == Token::identifier || + _tok._kind == Token::kw_exclude_file || + _tok._kind == Token::kw_sort_by_name || + _tok._kind == Token::kw_sort_by_alignment || + _tok._kind == Token::kw_sort_by_init_priority || + _tok._kind == Token::kw_sort_none) { + switch (_tok._kind) { + case Token::kw_exclude_file: { + auto vec = parseExcludeFile(); + if (vec.getError()) + return nullptr; + inputSections.insert(inputSections.end(), vec->begin(), vec->end()); + break; + } + case Token::star: + case Token::identifier: { + inputSections.push_back(new (_alloc) + InputSectionName(*this, _tok._range, false)); + consumeToken(); + break; + } + case Token::kw_sort_by_name: + case Token::kw_sort_by_alignment: + case Token::kw_sort_by_init_priority: + case Token::kw_sort_none: { + const InputSection *group = parseSortedInputSections(); + if (!group) + return nullptr; + inputSections.push_back(group); + break; + } + default: + llvm_unreachable("Unknown token"); + } + } + + for (int i = 0; i < numParen; ++i) + if (!expectAndConsume(Token::r_paren, "expected )")) + return nullptr; + return new (_alloc) + InputSectionsCmd(*this, memberName, archiveName, keep, fileSortMode, + archiveSortMode, inputSections); +} + +const OutputSectionDescription *Parser::parseOutputSectionDescription() { + assert((_tok._kind == Token::kw_discard || _tok._kind == Token::identifier) && + "Expected /DISCARD/ or identifier!"); + StringRef sectionName; + const Expression *address = nullptr; + const Expression *align = nullptr; + const Expression *subAlign = nullptr; + const Expression *at = nullptr; + const Expression *fillExpr = nullptr; + StringRef fillStream; + bool alignWithInput = false; + bool discard = false; + OutputSectionDescription::Constraint constraint = + OutputSectionDescription::C_None; + SmallVector<const Command *, 8> outputSectionCommands; + + if (_tok._kind == Token::kw_discard) + discard = true; + else + sectionName = _tok._range; + consumeToken(); + + if (_tok._kind == Token::number || _tok._kind == Token::identifier || + _tok._kind == Token::kw_align || _tok._kind == Token::l_paren) { + address = parseExpression(); + if (!address) + return nullptr; + } + + if (!expectAndConsume(Token::colon, "expected :")) + return nullptr; + + if (_tok._kind == Token::kw_at) { + consumeToken(); + at = parseExpression(); + if (!at) + return nullptr; + } + + if (_tok._kind == Token::kw_align) { + consumeToken(); + align = parseExpression(); + if (!align) + return nullptr; + } + + if (_tok._kind == Token::kw_align_with_input) { + consumeToken(); + alignWithInput = true; + } + + if (_tok._kind == Token::kw_subalign) { + consumeToken(); + subAlign = parseExpression(); + if (!subAlign) + return nullptr; + } + + if (_tok._kind == Token::kw_only_if_ro) { + consumeToken(); + constraint = OutputSectionDescription::C_OnlyIfRO; + } else if (_tok._kind == Token::kw_only_if_rw) { + consumeToken(); + constraint = OutputSectionDescription::C_OnlyIfRW; + } + + if (!expectAndConsume(Token::l_brace, "expected {")) + return nullptr; + + // Parse zero or more output-section-commands + while (_tok._kind != Token::r_brace) { + switch (_tok._kind) { + case Token::semicolon: + consumeToken(); + break; + case Token::identifier: + switch (peek()._kind) { + case Token::equal: + case Token::plusequal: + case Token::minusequal: + case Token::starequal: + case Token::slashequal: + case Token::ampequal: + case Token::pipeequal: + case Token::lesslessequal: + case Token::greatergreaterequal: + if (const Command *cmd = parseSymbolAssignment()) + outputSectionCommands.push_back(cmd); + else + return nullptr; + break; + default: + if (const Command *cmd = parseInputSectionsCmd()) + outputSectionCommands.push_back(cmd); + else + return nullptr; + break; + } + break; + case Token::kw_keep: + case Token::star: + case Token::colon: + case Token::kw_sort_by_name: + case Token::kw_sort_by_alignment: + case Token::kw_sort_by_init_priority: + case Token::kw_sort_none: + if (const Command *cmd = parseInputSectionsCmd()) + outputSectionCommands.push_back(cmd); + else + return nullptr; + break; + case Token::kw_hidden: + case Token::kw_provide: + case Token::kw_provide_hidden: + if (const Command *cmd = parseSymbolAssignment()) + outputSectionCommands.push_back(cmd); + else + return nullptr; + break; + default: + error(_tok, "expected symbol assignment or input file name."); + return nullptr; + } + } + + if (!expectAndConsume(Token::r_brace, "expected }")) + return nullptr; + + if (_tok._kind == Token::equal) { + consumeToken(); + if (_tok._kind != Token::number || !_tok._range.startswith_lower("0x")) { + fillExpr = parseExpression(); + if (!fillExpr) + return nullptr; + } else { + std::string strBuf; + if (isExpressionOperator(peek()) || + !parseHexToByteStream(_tok._range.drop_front(2), strBuf)) { + fillExpr = parseExpression(); + if(!fillExpr) + return nullptr; + } else { + char *rawBuf = (char *) _alloc.Allocate(strBuf.size(), 1); + memcpy(rawBuf, strBuf.c_str(), strBuf.size()); + fillStream = StringRef(rawBuf, strBuf.size()); + consumeToken(); + } + } + } + + return new (_alloc) OutputSectionDescription( + *this, sectionName, address, align, subAlign, at, fillExpr, fillStream, + alignWithInput, discard, constraint, outputSectionCommands); +} + +const Overlay *Parser::parseOverlay() { + assert(_tok._kind == Token::kw_overlay && "Expected OVERLAY!"); + error(_tok, "Overlay description is not yet supported."); + return nullptr; +} + +Sections *Parser::parseSections() { + assert(_tok._kind == Token::kw_sections && "Expected SECTIONS!"); + consumeToken(); + if (!expectAndConsume(Token::l_brace, "expected {")) + return nullptr; + SmallVector<const Command *, 8> sectionsCommands; + + bool unrecognizedToken = false; + // Parse zero or more sections-commands + while (!unrecognizedToken) { + switch (_tok._kind) { + case Token::semicolon: + consumeToken(); + break; + + case Token::identifier: + switch (peek()._kind) { + case Token::equal: + case Token::plusequal: + case Token::minusequal: + case Token::starequal: + case Token::slashequal: + case Token::ampequal: + case Token::pipeequal: + case Token::lesslessequal: + case Token::greatergreaterequal: + if (const Command *cmd = parseSymbolAssignment()) + sectionsCommands.push_back(cmd); + else + return nullptr; + break; + default: + if (const Command *cmd = parseOutputSectionDescription()) + sectionsCommands.push_back(cmd); + else + return nullptr; + break; + } + break; + + case Token::kw_discard: + case Token::star: + if (const Command *cmd = parseOutputSectionDescription()) + sectionsCommands.push_back(cmd); + else + return nullptr; + break; + + case Token::kw_entry: + if (const Command *cmd = parseEntry()) + sectionsCommands.push_back(cmd); + else + return nullptr; + break; + + case Token::kw_hidden: + case Token::kw_provide: + case Token::kw_provide_hidden: + if (const Command *cmd = parseSymbolAssignment()) + sectionsCommands.push_back(cmd); + else + return nullptr; + break; + + case Token::kw_overlay: + if (const Command *cmd = parseOverlay()) + sectionsCommands.push_back(cmd); + else + return nullptr; + break; + + default: + unrecognizedToken = true; + break; + } + } + + if (!expectAndConsume( + Token::r_brace, + "expected symbol assignment, entry, overlay or output section name.")) + return nullptr; + + return new (_alloc) Sections(*this, sectionsCommands); +} + +Memory *Parser::parseMemory() { + assert(_tok._kind == Token::kw_memory && "Expected MEMORY!"); + consumeToken(); + if (!expectAndConsume(Token::l_brace, "expected {")) + return nullptr; + SmallVector<const MemoryBlock *, 8> blocks; + + bool unrecognizedToken = false; + // Parse zero or more memory block descriptors. + while (!unrecognizedToken) { + if (_tok._kind == Token::identifier) { + StringRef name; + StringRef attrs; + const Expression *origin = nullptr; + const Expression *length = nullptr; + + name = _tok._range; + consumeToken(); + + // Parse optional memory region attributes. + if (_tok._kind == Token::l_paren) { + consumeToken(); + + if (_tok._kind != Token::identifier) { + error(_tok, "Expected memory attribute string."); + return nullptr; + } + attrs = _tok._range; + consumeToken(); + + if (!expectAndConsume(Token::r_paren, "expected )")) + return nullptr; + } + + if (!expectAndConsume(Token::colon, "expected :")) + return nullptr; + + // Parse the ORIGIN (base address of memory block). + if (!expectAndConsume(Token::kw_origin, "expected ORIGIN")) + return nullptr; + + if (!expectAndConsume(Token::equal, "expected =")) + return nullptr; + + origin = parseExpression(); + if (!origin) + return nullptr; + + if (!expectAndConsume(Token::comma, "expected ,")) + return nullptr; + + // Parse the LENGTH (length of memory block). + if (!expectAndConsume(Token::kw_length, "expected LENGTH")) + return nullptr; + + if (!expectAndConsume(Token::equal, "expected =")) + return nullptr; + + length = parseExpression(); + if (!length) + return nullptr; + + MemoryBlock *block = + new (_alloc) MemoryBlock(name, attrs, origin, length); + blocks.push_back(block); + } else { + unrecognizedToken = true; + } + } + if (!expectAndConsume( + Token::r_brace, + "expected memory block definition.")) + return nullptr; + + return new (_alloc) Memory(*this, blocks); +} + +Extern *Parser::parseExtern() { + assert(_tok._kind == Token::kw_extern && "Expected EXTERN!"); + consumeToken(); + if (!expectAndConsume(Token::l_paren, "expected (")) + return nullptr; + + // Parse one or more symbols. + SmallVector<StringRef, 8> symbols; + if (_tok._kind != Token::identifier) { + error(_tok, "expected one or more symbols in EXTERN."); + return nullptr; + } + symbols.push_back(_tok._range); + consumeToken(); + while (_tok._kind == Token::identifier) { + symbols.push_back(_tok._range); + consumeToken(); + } + + if (!expectAndConsume(Token::r_paren, "expected symbol in EXTERN.")) + return nullptr; + + return new (_alloc) Extern(*this, symbols); +} + +// Sema member functions +Sema::Sema() + : _scripts(), _layoutCommands(), _memberToLayoutOrder(), + _memberNameWildcards(), _cacheSectionOrder(), _cacheExpressionOrder(), + _deliveredExprs(), _symbolTable() {} + +void Sema::perform() { + for (auto &parser : _scripts) + perform(parser->get()); +} + +bool Sema::less(const SectionKey &lhs, const SectionKey &rhs) const { + int a = getLayoutOrder(lhs, true); + int b = getLayoutOrder(rhs, true); + + if (a != b) { + if (a < 0) + return false; + if (b < 0) + return true; + return a < b; + } + + // If both sections are not mapped anywhere, they have the same order + if (a < 0) + return false; + + // If both sections fall into the same layout order, we need to find their + // relative position as written in the (InputSectionsCmd). + return localCompare(a, lhs, rhs); +} + +StringRef Sema::getOutputSection(const SectionKey &key) const { + int layoutOrder = getLayoutOrder(key, true); + if (layoutOrder < 0) + return StringRef(); + + for (int i = layoutOrder - 1; i >= 0; --i) { + if (!isa<OutputSectionDescription>(_layoutCommands[i])) + continue; + + const OutputSectionDescription *out = + dyn_cast<OutputSectionDescription>(_layoutCommands[i]); + return out->name(); + } + + return StringRef(); +} + +std::vector<const SymbolAssignment *> +Sema::getExprs(const SectionKey &key) { + int layoutOrder = getLayoutOrder(key, false); + auto ans = std::vector<const SymbolAssignment *>(); + + if (layoutOrder < 0 || _deliveredExprs.count(layoutOrder) > 0) + return ans; + + for (int i = layoutOrder - 1; i >= 0; --i) { + if (isa<InputSection>(_layoutCommands[i])) + break; + if (auto assgn = dyn_cast<SymbolAssignment>(_layoutCommands[i])) + ans.push_back(assgn); + } + + // Reverse this order so we evaluate the expressions in the original order + // of the linker script + std::reverse(ans.begin(), ans.end()); + + // Mark this layout number as delivered + _deliveredExprs.insert(layoutOrder); + return ans; +} + +std::error_code Sema::evalExpr(const SymbolAssignment *assgn, + uint64_t &curPos) { + _symbolTable[StringRef(".")] = curPos; + + auto ans = assgn->expr()->evalExpr(_symbolTable); + if (ans.getError()) + return ans.getError(); + uint64_t result = *ans; + + if (assgn->symbol() == ".") { + curPos = result; + return std::error_code(); + } + + _symbolTable[assgn->symbol()] = result; + return std::error_code(); +} + +const llvm::StringSet<> &Sema::getScriptDefinedSymbols() const { + // Do we have cached results? + if (!_definedSymbols.empty()) + return _definedSymbols; + + // Populate our defined set and return it + for (auto cmd : _layoutCommands) + if (auto sa = dyn_cast<SymbolAssignment>(cmd)) { + StringRef symbol = sa->symbol(); + if (!symbol.empty() && symbol != ".") + _definedSymbols.insert(symbol); + } + + return _definedSymbols; +} + +uint64_t Sema::getLinkerScriptExprValue(StringRef name) const { + auto it = _symbolTable.find(name); + assert (it != _symbolTable.end() && "Invalid symbol name!"); + return it->second; +} + +void Sema::dump() const { + raw_ostream &os = llvm::outs(); + os << "Linker script semantics dump\n"; + int num = 0; + for (auto &parser : _scripts) { + os << "Dumping script #" << ++num << ":\n"; + parser->get()->dump(os); + os << "\n"; + } + os << "Dumping rule ids:\n"; + for (unsigned i = 0; i < _layoutCommands.size(); ++i) { + os << "LayoutOrder " << i << ":\n"; + _layoutCommands[i]->dump(os); + os << "\n\n"; + } +} + +/// Given a string "pattern" with wildcard characters, return true if it +/// matches "name". This function is useful when checking if a given name +/// pattern written in the linker script, i.e. ".text*", should match +/// ".text.anytext". +static bool wildcardMatch(StringRef pattern, StringRef name) { + auto i = name.begin(); + + // Check if each char in pattern also appears in our input name, handling + // special wildcard characters. + for (auto j = pattern.begin(), e = pattern.end(); j != e; ++j) { + if (i == name.end()) + return false; + + switch (*j) { + case '*': + while (!wildcardMatch(pattern.drop_front(j - pattern.begin() + 1), + name.drop_front(i - name.begin() + 1))) { + if (i == name.end()) + return false; + ++i; + } + break; + case '?': + // Matches any character + break; + case '[': { + // Matches a range of characters specified between brackets + size_t end = pattern.find(']', j - pattern.begin()); + if (end == pattern.size()) + return false; + + StringRef chars = pattern.slice(j - pattern.begin(), end); + if (chars.find(i) == StringRef::npos) + return false; + + j = pattern.begin() + end; + break; + } + case '\\': + ++j; + if (*j != *i) + return false; + break; + default: + // No wildcard character means we must match exactly the same char + if (*j != *i) + return false; + break; + } + ++i; + } + + // If our pattern has't consumed the entire string, it is not a match + return i == name.end(); +} + +int Sema::matchSectionName(int id, const SectionKey &key) const { + const InputSectionsCmd *cmd = dyn_cast<InputSectionsCmd>(_layoutCommands[id]); + + if (!cmd || !wildcardMatch(cmd->archiveName(), key.archivePath)) + return -1; + + while ((size_t)++id < _layoutCommands.size() && + (isa<InputSection>(_layoutCommands[id]))) { + if (isa<InputSectionSortedGroup>(_layoutCommands[id])) + continue; + + const InputSectionName *in = + dyn_cast<InputSectionName>(_layoutCommands[id]); + if (wildcardMatch(in->name(), key.sectionName)) + return id; + } + return -1; +} + +int Sema::getLayoutOrder(const SectionKey &key, bool coarse) const { + // First check if we already answered this layout question + if (coarse) { + auto entry = _cacheSectionOrder.find(key); + if (entry != _cacheSectionOrder.end()) + return entry->second; + } else { + auto entry = _cacheExpressionOrder.find(key); + if (entry != _cacheExpressionOrder.end()) + return entry->second; + } + + // Try to match exact file name + auto range = _memberToLayoutOrder.equal_range(key.memberPath); + for (auto I = range.first, E = range.second; I != E; ++I) { + int order = I->second; + int exprOrder = -1; + + if ((exprOrder = matchSectionName(order, key)) >= 0) { + if (coarse) { + _cacheSectionOrder.insert(std::make_pair(key, order)); + return order; + } + _cacheExpressionOrder.insert(std::make_pair(key, exprOrder)); + return exprOrder; + } + } + + // If we still couldn't find a rule for this input section, try to match + // wildcards + for (auto I = _memberNameWildcards.begin(), E = _memberNameWildcards.end(); + I != E; ++I) { + if (!wildcardMatch(I->first, key.memberPath)) + continue; + int order = I->second; + int exprOrder = -1; + + if ((exprOrder = matchSectionName(order, key)) >= 0) { + if (coarse) { + _cacheSectionOrder.insert(std::make_pair(key, order)); + return order; + } + _cacheExpressionOrder.insert(std::make_pair(key, exprOrder)); + return exprOrder; + } + } + + _cacheSectionOrder.insert(std::make_pair(key, -1)); + _cacheExpressionOrder.insert(std::make_pair(key, -1)); + return -1; +} + +static bool compareSortedNames(WildcardSortMode sortMode, StringRef lhs, + StringRef rhs) { + switch (sortMode) { + case WildcardSortMode::None: + case WildcardSortMode::NA: + return false; + case WildcardSortMode::ByAlignment: + case WildcardSortMode::ByInitPriority: + case WildcardSortMode::ByAlignmentAndName: + assert(false && "Unimplemented sort order"); + break; + case WildcardSortMode::ByName: + return lhs.compare(rhs) < 0; + case WildcardSortMode::ByNameAndAlignment: + int compare = lhs.compare(rhs); + if (compare != 0) + return compare < 0; + return compareSortedNames(WildcardSortMode::ByAlignment, lhs, rhs); + } + return false; +} + +static bool sortedGroupContains(const InputSectionSortedGroup *cmd, + const Sema::SectionKey &key) { + for (const InputSection *child : *cmd) { + if (auto i = dyn_cast<InputSectionName>(child)) { + if (wildcardMatch(i->name(), key.sectionName)) + return true; + continue; + } + + auto *sortedGroup = dyn_cast<InputSectionSortedGroup>(child); + assert(sortedGroup && "Expected InputSectionSortedGroup object"); + + if (sortedGroupContains(sortedGroup, key)) + return true; + } + + return false; +} + +bool Sema::localCompare(int order, const SectionKey &lhs, + const SectionKey &rhs) const { + const InputSectionsCmd *cmd = + dyn_cast<InputSectionsCmd>(_layoutCommands[order]); + + assert(cmd && "Invalid InputSectionsCmd index"); + + if (lhs.archivePath != rhs.archivePath) + return compareSortedNames(cmd->archiveSortMode(), lhs.archivePath, + rhs.archivePath); + + if (lhs.memberPath != rhs.memberPath) + return compareSortedNames(cmd->fileSortMode(), lhs.memberPath, + rhs.memberPath); + + // Both sections come from the same exact same file and rule. Start walking + // through input section names as written in the linker script and the + // first one to match will have higher priority. + for (const InputSection *inputSection : *cmd) { + if (auto i = dyn_cast<InputSectionName>(inputSection)) { + // If both match, return false (both have equal priority) + // If rhs match, return false (rhs has higher priority) + if (wildcardMatch(i->name(), rhs.sectionName)) + return false; + // If lhs matches first, it has priority over rhs + if (wildcardMatch(i->name(), lhs.sectionName)) + return true; + continue; + } + + // Handle sorted subgroups specially + auto *sortedGroup = dyn_cast<InputSectionSortedGroup>(inputSection); + assert(sortedGroup && "Expected InputSectionSortedGroup object"); + + bool a = sortedGroupContains(sortedGroup, lhs); + bool b = sortedGroupContains(sortedGroup, rhs); + if (a && !b) + return false; + if (b && !a) + return true; + if (!a && !a) + continue; + + return compareSortedNames(sortedGroup->sortMode(), lhs.sectionName, + rhs.sectionName); + } + + llvm_unreachable(""); + return false; +} + +static bool hasWildcard(StringRef name) { + for (auto ch : name) + if (ch == '*' || ch == '?' || ch == '[' || ch == '\\') + return true; + return false; +} + +void Sema::linearizeAST(const InputSection *inputSection) { + if (isa<InputSectionName>(inputSection)) { + _layoutCommands.push_back(inputSection); + return; + } + + auto *sortedGroup = dyn_cast<InputSectionSortedGroup>(inputSection); + assert(sortedGroup && "Expected InputSectionSortedGroup object"); + + for (const InputSection *child : *sortedGroup) { + linearizeAST(child); + } +} + +void Sema::linearizeAST(const InputSectionsCmd *inputSections) { + StringRef memberName = inputSections->memberName(); + // Populate our maps for fast lookup of InputSectionsCmd + if (hasWildcard(memberName)) + _memberNameWildcards.push_back( + std::make_pair(memberName, (int)_layoutCommands.size())); + else if (!memberName.empty()) + _memberToLayoutOrder.insert( + std::make_pair(memberName.str(), (int)_layoutCommands.size())); + + _layoutCommands.push_back(inputSections); + for (const InputSection *inputSection : *inputSections) + linearizeAST(inputSection); +} + +void Sema::linearizeAST(const Sections *sections) { + for (const Command *sectionCommand : *sections) { + if (isa<SymbolAssignment>(sectionCommand)) { + _layoutCommands.push_back(sectionCommand); + continue; + } + + if (!isa<OutputSectionDescription>(sectionCommand)) + continue; + + _layoutCommands.push_back(sectionCommand); + auto *outSection = dyn_cast<OutputSectionDescription>(sectionCommand); + + for (const Command *outSecCommand : *outSection) { + if (isa<SymbolAssignment>(outSecCommand)) { + _layoutCommands.push_back(outSecCommand); + continue; + } + + if (!isa<InputSectionsCmd>(outSecCommand)) + continue; + + linearizeAST(dyn_cast<InputSectionsCmd>(outSecCommand)); + } + } +} + +void Sema::perform(const LinkerScript *ls) { + for (const Command *c : ls->_commands) { + if (const Sections *sec = dyn_cast<Sections>(c)) + linearizeAST(sec); + } +} + +} // End namespace script +} // end namespace lld diff --git a/lib/ReaderWriter/MachO/ArchHandler.cpp b/lib/ReaderWriter/MachO/ArchHandler.cpp new file mode 100644 index 0000000000000..cb20907b3e30d --- /dev/null +++ b/lib/ReaderWriter/MachO/ArchHandler.cpp @@ -0,0 +1,172 @@ +//===- lib/FileFormat/MachO/ArchHandler.cpp -------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "ArchHandler.h" +#include "Atoms.h" +#include "MachONormalizedFileBinaryUtils.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace llvm::MachO; +using namespace lld::mach_o::normalized; + +namespace lld { +namespace mach_o { + + +ArchHandler::ArchHandler() { +} + +ArchHandler::~ArchHandler() { +} + +std::unique_ptr<mach_o::ArchHandler> ArchHandler::create( + MachOLinkingContext::Arch arch) { + switch (arch) { + case MachOLinkingContext::arch_x86_64: + return create_x86_64(); + case MachOLinkingContext::arch_x86: + return create_x86(); + case MachOLinkingContext::arch_armv6: + case MachOLinkingContext::arch_armv7: + case MachOLinkingContext::arch_armv7s: + return create_arm(); + case MachOLinkingContext::arch_arm64: + return create_arm64(); + default: + llvm_unreachable("Unknown arch"); + } +} + + +bool ArchHandler::isLazyPointer(const Reference &ref) { + // A lazy bind entry is needed for a lazy pointer. + const StubInfo &info = stubInfo(); + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) + return false; + if (ref.kindArch() != info.lazyPointerReferenceToFinal.arch) + return false; + return (ref.kindValue() == info.lazyPointerReferenceToFinal.kind); +} + + +ArchHandler::RelocPattern ArchHandler::relocPattern(const Relocation &reloc) { + assert((reloc.type & 0xFFF0) == 0); + uint16_t result = reloc.type; + if (reloc.scattered) + result |= rScattered; + if (reloc.pcRel) + result |= rPcRel; + if (reloc.isExtern) + result |= rExtern; + switch(reloc.length) { + case 0: + break; + case 1: + result |= rLength2; + break; + case 2: + result |= rLength4; + break; + case 3: + result |= rLength8; + break; + default: + llvm_unreachable("bad r_length"); + } + return result; +} + +normalized::Relocation +ArchHandler::relocFromPattern(ArchHandler::RelocPattern pattern) { + normalized::Relocation result; + result.offset = 0; + result.scattered = (pattern & rScattered); + result.type = (RelocationInfoType)(pattern & 0xF); + result.pcRel = (pattern & rPcRel); + result.isExtern = (pattern & rExtern); + result.value = 0; + result.symbol = 0; + switch (pattern & 0x300) { + case rLength1: + result.length = 0; + break; + case rLength2: + result.length = 1; + break; + case rLength4: + result.length = 2; + break; + case rLength8: + result.length = 3; + break; + } + return result; +} + +void ArchHandler::appendReloc(normalized::Relocations &relocs, uint32_t offset, + uint32_t symbol, uint32_t value, + RelocPattern pattern) { + normalized::Relocation reloc = relocFromPattern(pattern); + reloc.offset = offset; + reloc.symbol = symbol; + reloc.value = value; + relocs.push_back(reloc); +} + + +int16_t ArchHandler::readS16(const uint8_t *addr, bool isBig) { + return read16(addr, isBig); +} + +int32_t ArchHandler::readS32(const uint8_t *addr, bool isBig) { + return read32(addr, isBig); +} + +uint32_t ArchHandler::readU32(const uint8_t *addr, bool isBig) { + return read32(addr, isBig); +} + + int64_t ArchHandler::readS64(const uint8_t *addr, bool isBig) { + return read64(addr, isBig); +} + +bool ArchHandler::isDwarfCIE(bool isBig, const DefinedAtom *atom) { + assert(atom->contentType() == DefinedAtom::typeCFI); + if (atom->rawContent().size() < sizeof(uint32_t)) + return false; + uint32_t size = read32(atom->rawContent().data(), isBig); + + uint32_t idOffset = sizeof(uint32_t); + if (size == 0xffffffffU) + idOffset += sizeof(uint64_t); + + return read32(atom->rawContent().data() + idOffset, isBig) == 0; +} + +const Atom *ArchHandler::fdeTargetFunction(const DefinedAtom *fde) { + for (auto ref : *fde) { + if (ref->kindNamespace() == Reference::KindNamespace::mach_o && + ref->kindValue() == unwindRefToFunctionKind()) { + assert(ref->kindArch() == kindArch() && "unexpected Reference arch"); + return ref->target(); + } + } + + return nullptr; +} + +} // namespace mach_o +} // namespace lld + + + diff --git a/lib/ReaderWriter/MachO/ArchHandler.h b/lib/ReaderWriter/MachO/ArchHandler.h new file mode 100644 index 0000000000000..7f0961ebc807e --- /dev/null +++ b/lib/ReaderWriter/MachO/ArchHandler.h @@ -0,0 +1,300 @@ +//===- lib/FileFormat/MachO/ArchHandler.h ---------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Atoms.h" +#include "File.h" +#include "MachONormalizedFile.h" +#include "lld/Core/LLVM.h" +#include "lld/Core/Reference.h" +#include "lld/Core/Simple.h" +#include "lld/ReaderWriter/MachOLinkingContext.h" +#include "llvm/ADT/Triple.h" + +#ifndef LLD_READER_WRITER_MACHO_ARCH_HANDLER_H +#define LLD_READER_WRITER_MACHO_ARCH_HANDLER_H + +namespace lld { +namespace mach_o { + +/// +/// The ArchHandler class handles all architecture specific aspects of +/// mach-o linking. +/// +class ArchHandler { +public: + virtual ~ArchHandler(); + + /// There is no public interface to subclasses of ArchHandler, so this + /// is the only way to instantiate an ArchHandler. + static std::unique_ptr<ArchHandler> create(MachOLinkingContext::Arch arch); + + /// Get (arch specific) kind strings used by Registry. + virtual const Registry::KindStrings *kindStrings() = 0; + + /// Convert mach-o Arch to Reference::KindArch. + virtual Reference::KindArch kindArch() = 0; + + /// Used by StubPass to update References to shared library functions + /// to be references to a stub. + virtual bool isCallSite(const Reference &) = 0; + + /// Used by GOTPass to locate GOT References + virtual bool isGOTAccess(const Reference &, bool &canBypassGOT) { + return false; + } + + /// Used by ShimPass to insert shims in branches that switch mode. + virtual bool isNonCallBranch(const Reference &) = 0; + + /// Used by GOTPass to update GOT References + virtual void updateReferenceToGOT(const Reference *, bool targetIsNowGOT) {} + + /// Does this architecture make use of __unwind_info sections for exception + /// handling? If so, it will need a separate pass to create them. + virtual bool needsCompactUnwind() = 0; + + /// Returns the kind of reference to use to synthesize a 32-bit image-offset + /// value, used in the __unwind_info section. + virtual Reference::KindValue imageOffsetKind() = 0; + + /// Returns the kind of reference to use to synthesize a 32-bit image-offset + /// indirect value. Used for personality functions in the __unwind_info + /// section. + virtual Reference::KindValue imageOffsetKindIndirect() = 0; + + /// Architecture specific compact unwind type that signals __eh_frame should + /// actually be used. + virtual uint32_t dwarfCompactUnwindType() = 0; + + /// Reference from an __eh_frame FDE to the CIE it's based on. + virtual Reference::KindValue unwindRefToCIEKind() = 0; + + /// Reference from an __eh_frame FDE atom to the function it's + /// describing. Usually pointer-sized and PC-relative, but differs in whether + /// it needs to be in relocatable objects. + virtual Reference::KindValue unwindRefToFunctionKind() = 0; + + /// Reference from an __unwind_info entry of dwarfCompactUnwindType to the + /// required __eh_frame entry. On current architectures, the low 24 bits + /// represent the offset of the function's FDE entry from the start of + /// __eh_frame. + virtual Reference::KindValue unwindRefToEhFrameKind() = 0; + + virtual const Atom *fdeTargetFunction(const DefinedAtom *fde); + + /// Used by normalizedFromAtoms() to know where to generated rebasing and + /// binding info in final executables. + virtual bool isPointer(const Reference &) = 0; + + /// Used by normalizedFromAtoms() to know where to generated lazy binding + /// info in final executables. + virtual bool isLazyPointer(const Reference &); + + /// Returns true if the specified relocation is paired to the next relocation. + virtual bool isPairedReloc(const normalized::Relocation &) = 0; + + /// Prototype for a helper function. Given a sectionIndex and address, + /// finds the atom and offset with that atom of that address. + typedef std::function<std::error_code (uint32_t sectionIndex, uint64_t addr, + const lld::Atom **, Reference::Addend *)> + FindAtomBySectionAndAddress; + + /// Prototype for a helper function. Given a symbolIndex, finds the atom + /// representing that symbol. + typedef std::function<std::error_code (uint32_t symbolIndex, + const lld::Atom **)> FindAtomBySymbolIndex; + + /// Analyzes a relocation from a .o file and returns the info + /// (kind, target, addend) needed to instantiate a Reference. + /// Two helper functions are passed as parameters to find the target atom + /// given a symbol index or address. + virtual std::error_code + getReferenceInfo(const normalized::Relocation &reloc, + const DefinedAtom *inAtom, + uint32_t offsetInAtom, + uint64_t fixupAddress, bool isBigEndian, + FindAtomBySectionAndAddress atomFromAddress, + FindAtomBySymbolIndex atomFromSymbolIndex, + Reference::KindValue *kind, + const lld::Atom **target, + Reference::Addend *addend) = 0; + + /// Analyzes a pair of relocations from a .o file and returns the info + /// (kind, target, addend) needed to instantiate a Reference. + /// Two helper functions are passed as parameters to find the target atom + /// given a symbol index or address. + virtual std::error_code + getPairReferenceInfo(const normalized::Relocation &reloc1, + const normalized::Relocation &reloc2, + const DefinedAtom *inAtom, + uint32_t offsetInAtom, + uint64_t fixupAddress, bool isBig, bool scatterable, + FindAtomBySectionAndAddress atomFromAddress, + FindAtomBySymbolIndex atomFromSymbolIndex, + Reference::KindValue *kind, + const lld::Atom **target, + Reference::Addend *addend) = 0; + + /// Prototype for a helper function. Given an atom, finds the symbol table + /// index for it in the output file. + typedef std::function<uint32_t (const Atom &atom)> FindSymbolIndexForAtom; + + /// Prototype for a helper function. Given an atom, finds the index + /// of the section that will contain the atom. + typedef std::function<uint32_t (const Atom &atom)> FindSectionIndexForAtom; + + /// Prototype for a helper function. Given an atom, finds the address + /// assigned to it in the output file. + typedef std::function<uint64_t (const Atom &atom)> FindAddressForAtom; + + /// Some architectures require local symbols on anonymous atoms. + virtual bool needsLocalSymbolInRelocatableFile(const DefinedAtom *atom) { + return false; + } + + /// Copy raw content then apply all fixup References on an Atom. + virtual void generateAtomContent(const DefinedAtom &atom, bool relocatable, + FindAddressForAtom findAddress, + FindAddressForAtom findSectionAddress, + uint64_t imageBaseAddress, + uint8_t *atomContentBuffer) = 0; + + /// Used in -r mode to convert a Reference to a mach-o relocation. + virtual void appendSectionRelocations(const DefinedAtom &atom, + uint64_t atomSectionOffset, + const Reference &ref, + FindSymbolIndexForAtom, + FindSectionIndexForAtom, + FindAddressForAtom, + normalized::Relocations&) = 0; + + /// Add arch-specific References. + virtual void addAdditionalReferences(MachODefinedAtom &atom) { } + + // Add Reference for data-in-code marker. + virtual void addDataInCodeReference(MachODefinedAtom &atom, uint32_t atomOff, + uint16_t length, uint16_t kind) { } + + /// Returns true if the specificed Reference value marks the start or end + /// of a data-in-code range in an atom. + virtual bool isDataInCodeTransition(Reference::KindValue refKind) { + return false; + } + + /// Returns the Reference value for a Reference that marks that start of + /// a data-in-code range. + virtual Reference::KindValue dataInCodeTransitionStart( + const MachODefinedAtom &atom) { + return 0; + } + + /// Returns the Reference value for a Reference that marks that end of + /// a data-in-code range. + virtual Reference::KindValue dataInCodeTransitionEnd( + const MachODefinedAtom &atom) { + return 0; + } + + /// Only relevant for 32-bit arm archs. + virtual bool isThumbFunction(const DefinedAtom &atom) { return false; } + + /// Only relevant for 32-bit arm archs. + virtual const DefinedAtom *createShim(MachOFile &file, bool thumbToArm, + const DefinedAtom &) { + llvm_unreachable("shims only support on arm"); + } + + /// Does a given unwind-cfi atom represent a CIE (as opposed to an FDE). + static bool isDwarfCIE(bool isBig, const DefinedAtom *atom); + + struct ReferenceInfo { + Reference::KindArch arch; + uint16_t kind; + uint32_t offset; + int32_t addend; + }; + + struct OptionalRefInfo { + bool used; + uint16_t kind; + uint32_t offset; + int32_t addend; + }; + + /// Table of architecture specific information for creating stubs. + struct StubInfo { + const char* binderSymbolName; + ReferenceInfo lazyPointerReferenceToHelper; + ReferenceInfo lazyPointerReferenceToFinal; + ReferenceInfo nonLazyPointerReferenceToBinder; + uint8_t codeAlignment; + + uint32_t stubSize; + uint8_t stubBytes[16]; + ReferenceInfo stubReferenceToLP; + OptionalRefInfo optStubReferenceToLP; + + uint32_t stubHelperSize; + uint8_t stubHelperBytes[16]; + ReferenceInfo stubHelperReferenceToImm; + ReferenceInfo stubHelperReferenceToHelperCommon; + + uint32_t stubHelperCommonSize; + uint8_t stubHelperCommonBytes[36]; + ReferenceInfo stubHelperCommonReferenceToCache; + OptionalRefInfo optStubHelperCommonReferenceToCache; + ReferenceInfo stubHelperCommonReferenceToBinder; + OptionalRefInfo optStubHelperCommonReferenceToBinder; + }; + + virtual const StubInfo &stubInfo() = 0; + +protected: + ArchHandler(); + + static std::unique_ptr<mach_o::ArchHandler> create_x86_64(); + static std::unique_ptr<mach_o::ArchHandler> create_x86(); + static std::unique_ptr<mach_o::ArchHandler> create_arm(); + static std::unique_ptr<mach_o::ArchHandler> create_arm64(); + + // Handy way to pack mach-o r_type and other bit fields into one 16-bit value. + typedef uint16_t RelocPattern; + enum { + rScattered = 0x8000, + rPcRel = 0x4000, + rExtern = 0x2000, + rLength1 = 0x0000, + rLength2 = 0x0100, + rLength4 = 0x0200, + rLength8 = 0x0300, + rLenArmLo = rLength1, + rLenArmHi = rLength2, + rLenThmbLo = rLength4, + rLenThmbHi = rLength8 + }; + /// Extract RelocPattern from normalized mach-o relocation. + static RelocPattern relocPattern(const normalized::Relocation &reloc); + /// Create normalized Relocation initialized from pattern. + static normalized::Relocation relocFromPattern(RelocPattern pattern); + /// One liner to add a relocation. + static void appendReloc(normalized::Relocations &relocs, uint32_t offset, + uint32_t symbol, uint32_t value, + RelocPattern pattern); + + + static int16_t readS16(const uint8_t *addr, bool isBig); + static int32_t readS32(const uint8_t *addr, bool isBig); + static uint32_t readU32(const uint8_t *addr, bool isBig); + static int64_t readS64(const uint8_t *addr, bool isBig); +}; + +} // namespace mach_o +} // namespace lld + +#endif // LLD_READER_WRITER_MACHO_ARCH_HANDLER_H diff --git a/lib/ReaderWriter/MachO/ArchHandler_arm.cpp b/lib/ReaderWriter/MachO/ArchHandler_arm.cpp new file mode 100644 index 0000000000000..43f88a1d30d84 --- /dev/null +++ b/lib/ReaderWriter/MachO/ArchHandler_arm.cpp @@ -0,0 +1,1524 @@ +//===- lib/FileFormat/MachO/ArchHandler_arm.cpp ---------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ArchHandler.h" +#include "Atoms.h" +#include "MachONormalizedFileBinaryUtils.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace llvm::MachO; +using namespace lld::mach_o::normalized; + +namespace lld { +namespace mach_o { + +using llvm::support::ulittle32_t; +using llvm::support::little32_t; + + +class ArchHandler_arm : public ArchHandler { +public: + ArchHandler_arm(); + virtual ~ArchHandler_arm(); + + const Registry::KindStrings *kindStrings() override { return _sKindStrings; } + + Reference::KindArch kindArch() override { return Reference::KindArch::ARM; } + + const ArchHandler::StubInfo &stubInfo() override; + bool isCallSite(const Reference &) override; + bool isPointer(const Reference &) override; + bool isPairedReloc(const normalized::Relocation &) override; + bool isNonCallBranch(const Reference &) override; + + bool needsCompactUnwind() override { + return false; + } + Reference::KindValue imageOffsetKind() override { + return invalid; + } + Reference::KindValue imageOffsetKindIndirect() override { + return invalid; + } + + Reference::KindValue unwindRefToCIEKind() override { + return invalid; + } + + Reference::KindValue unwindRefToFunctionKind() override { + return invalid; + } + + Reference::KindValue unwindRefToEhFrameKind() override { + return invalid; + } + + uint32_t dwarfCompactUnwindType() override { + // FIXME + return -1; + } + + std::error_code getReferenceInfo(const normalized::Relocation &reloc, + const DefinedAtom *inAtom, + uint32_t offsetInAtom, + uint64_t fixupAddress, bool swap, + FindAtomBySectionAndAddress atomFromAddress, + FindAtomBySymbolIndex atomFromSymbolIndex, + Reference::KindValue *kind, + const lld::Atom **target, + Reference::Addend *addend) override; + std::error_code + getPairReferenceInfo(const normalized::Relocation &reloc1, + const normalized::Relocation &reloc2, + const DefinedAtom *inAtom, + uint32_t offsetInAtom, + uint64_t fixupAddress, bool swap, bool scatterable, + FindAtomBySectionAndAddress atomFromAddress, + FindAtomBySymbolIndex atomFromSymbolIndex, + Reference::KindValue *kind, + const lld::Atom **target, + Reference::Addend *addend) override; + + void generateAtomContent(const DefinedAtom &atom, bool relocatable, + FindAddressForAtom findAddress, + FindAddressForAtom findSectionAddress, + uint64_t imageBaseAddress, + uint8_t *atomContentBuffer) override; + + void appendSectionRelocations(const DefinedAtom &atom, + uint64_t atomSectionOffset, + const Reference &ref, + FindSymbolIndexForAtom, + FindSectionIndexForAtom, + FindAddressForAtom, + normalized::Relocations &) override; + + void addAdditionalReferences(MachODefinedAtom &atom) override; + + bool isDataInCodeTransition(Reference::KindValue refKind) override { + switch (refKind) { + case modeThumbCode: + case modeArmCode: + case modeData: + return true; + default: + return false; + break; + } + } + + Reference::KindValue dataInCodeTransitionStart( + const MachODefinedAtom &atom) override { + return modeData; + } + + Reference::KindValue dataInCodeTransitionEnd( + const MachODefinedAtom &atom) override { + return atom.isThumb() ? modeThumbCode : modeArmCode; + } + + bool isThumbFunction(const DefinedAtom &atom) override; + const DefinedAtom *createShim(MachOFile &file, bool thumbToArm, + const DefinedAtom &) override; + +private: + friend class Thumb2ToArmShimAtom; + friend class ArmToThumbShimAtom; + + static const Registry::KindStrings _sKindStrings[]; + static const StubInfo _sStubInfoArmPIC; + + enum ArmKind : Reference::KindValue { + invalid, /// for error condition + + modeThumbCode, /// Content starting at this offset is thumb. + modeArmCode, /// Content starting at this offset is arm. + modeData, /// Content starting at this offset is data. + + // Kinds found in mach-o .o files: + thumb_bl22, /// ex: bl _foo + thumb_b22, /// ex: b _foo + thumb_movw, /// ex: movw r1, :lower16:_foo + thumb_movt, /// ex: movt r1, :lower16:_foo + thumb_movw_funcRel, /// ex: movw r1, :lower16:(_foo-(L1+4)) + thumb_movt_funcRel, /// ex: movt r1, :upper16:(_foo-(L1+4)) + arm_bl24, /// ex: bl _foo + arm_b24, /// ex: b _foo + arm_movw, /// ex: movw r1, :lower16:_foo + arm_movt, /// ex: movt r1, :lower16:_foo + arm_movw_funcRel, /// ex: movw r1, :lower16:(_foo-(L1+4)) + arm_movt_funcRel, /// ex: movt r1, :upper16:(_foo-(L1+4)) + pointer32, /// ex: .long _foo + delta32, /// ex: .long _foo - . + + // Kinds introduced by Passes: + lazyPointer, /// Location contains a lazy pointer. + lazyImmediateLocation, /// Location contains immediate value used in stub. + }; + + // Utility functions for inspecting/updating instructions. + static bool isThumbMovw(uint32_t instruction); + static bool isThumbMovt(uint32_t instruction); + static bool isArmMovw(uint32_t instruction); + static bool isArmMovt(uint32_t instruction); + static int32_t getDisplacementFromThumbBranch(uint32_t instruction, uint32_t); + static int32_t getDisplacementFromArmBranch(uint32_t instruction); + static uint16_t getWordFromThumbMov(uint32_t instruction); + static uint16_t getWordFromArmMov(uint32_t instruction); + static uint32_t clearThumbBit(uint32_t value, const Atom *target); + static uint32_t setDisplacementInArmBranch(uint32_t instr, int32_t disp, + bool targetIsThumb); + static uint32_t setDisplacementInThumbBranch(uint32_t instr, uint32_t ia, + int32_t disp, bool targetThumb); + static uint32_t setWordFromThumbMov(uint32_t instruction, uint16_t word); + static uint32_t setWordFromArmMov(uint32_t instruction, uint16_t word); + + StringRef stubName(const DefinedAtom &); + bool useExternalRelocationTo(const Atom &target); + + void applyFixupFinal(const Reference &ref, uint8_t *location, + uint64_t fixupAddress, uint64_t targetAddress, + uint64_t inAtomAddress, bool &thumbMode, + bool targetIsThumb); + + void applyFixupRelocatable(const Reference &ref, uint8_t *location, + uint64_t fixupAddress, + uint64_t targetAddress, + uint64_t inAtomAddress, bool &thumbMode, + bool targetIsThumb); +}; + +//===----------------------------------------------------------------------===// +// ArchHandler_arm +//===----------------------------------------------------------------------===// + +ArchHandler_arm::ArchHandler_arm() { } + +ArchHandler_arm::~ArchHandler_arm() { } + +const Registry::KindStrings ArchHandler_arm::_sKindStrings[] = { + LLD_KIND_STRING_ENTRY(invalid), + LLD_KIND_STRING_ENTRY(modeThumbCode), + LLD_KIND_STRING_ENTRY(modeArmCode), + LLD_KIND_STRING_ENTRY(modeData), + LLD_KIND_STRING_ENTRY(thumb_bl22), + LLD_KIND_STRING_ENTRY(thumb_b22), + LLD_KIND_STRING_ENTRY(thumb_movw), + LLD_KIND_STRING_ENTRY(thumb_movt), + LLD_KIND_STRING_ENTRY(thumb_movw_funcRel), + LLD_KIND_STRING_ENTRY(thumb_movt_funcRel), + LLD_KIND_STRING_ENTRY(arm_bl24), + LLD_KIND_STRING_ENTRY(arm_b24), + LLD_KIND_STRING_ENTRY(arm_movw), + LLD_KIND_STRING_ENTRY(arm_movt), + LLD_KIND_STRING_ENTRY(arm_movw_funcRel), + LLD_KIND_STRING_ENTRY(arm_movt_funcRel), + LLD_KIND_STRING_ENTRY(pointer32), + LLD_KIND_STRING_ENTRY(delta32), + LLD_KIND_STRING_ENTRY(lazyPointer), + LLD_KIND_STRING_ENTRY(lazyImmediateLocation), + LLD_KIND_STRING_END +}; + +const ArchHandler::StubInfo ArchHandler_arm::_sStubInfoArmPIC = { + "dyld_stub_binder", + + // References in lazy pointer + { Reference::KindArch::ARM, pointer32, 0, 0 }, + { Reference::KindArch::ARM, lazyPointer, 0, 0 }, + + // GOT pointer to dyld_stub_binder + { Reference::KindArch::ARM, pointer32, 0, 0 }, + + // arm code alignment 2^2 + 2, + + // Stub size and code + 16, + { 0x04, 0xC0, 0x9F, 0xE5, // ldr ip, pc + 12 + 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip + 0x00, 0xF0, 0x9C, 0xE5, // ldr pc, [ip] + 0x00, 0x00, 0x00, 0x00 }, // .long L_foo$lazy_ptr - (L1$scv + 8) + { Reference::KindArch::ARM, delta32, 12, 0 }, + { false, 0, 0, 0 }, + + // Stub Helper size and code + 12, + { 0x00, 0xC0, 0x9F, 0xE5, // ldr ip, [pc, #0] + 0x00, 0x00, 0x00, 0xEA, // b _helperhelper + 0x00, 0x00, 0x00, 0x00 }, // .long lazy-info-offset + { Reference::KindArch::ARM, lazyImmediateLocation, 8, 0 }, + { Reference::KindArch::ARM, arm_b24, 4, 0 }, + + // Stub Helper-Common size and code + 36, + { // push lazy-info-offset + 0x04, 0xC0, 0x2D, 0xE5, // str ip, [sp, #-4]! + // push address of dyld_mageLoaderCache + 0x10, 0xC0, 0x9F, 0xE5, // ldr ip, L1 + 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip + 0x04, 0xC0, 0x2D, 0xE5, // str ip, [sp, #-4]! + // jump through dyld_stub_binder + 0x08, 0xC0, 0x9F, 0xE5, // ldr ip, L2 + 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip + 0x00, 0xF0, 0x9C, 0xE5, // ldr pc, [ip] + 0x00, 0x00, 0x00, 0x00, // L1: .long fFastStubGOTAtom - (helper+16) + 0x00, 0x00, 0x00, 0x00 }, // L2: .long dyld_stub_binder - (helper+28) + { Reference::KindArch::ARM, delta32, 28, 0xC }, + { false, 0, 0, 0 }, + { Reference::KindArch::ARM, delta32, 32, 0x04 }, + { false, 0, 0, 0 } +}; + +const ArchHandler::StubInfo &ArchHandler_arm::stubInfo() { + // If multiple kinds of stubs are supported, select which StubInfo here. + return _sStubInfoArmPIC; +} + +bool ArchHandler_arm::isCallSite(const Reference &ref) { + switch (ref.kindValue()) { + case thumb_b22: + case thumb_bl22: + case arm_b24: + case arm_bl24: + return true; + default: + return false; + } +} + +bool ArchHandler_arm::isPointer(const Reference &ref) { + return (ref.kindValue() == pointer32); +} + +bool ArchHandler_arm::isNonCallBranch(const Reference &ref) { + switch (ref.kindValue()) { + case thumb_b22: + case arm_b24: + return true; + default: + return false; + } +} + +bool ArchHandler_arm::isPairedReloc(const Relocation &reloc) { + switch (reloc.type) { + case ARM_RELOC_SECTDIFF: + case ARM_RELOC_LOCAL_SECTDIFF: + case ARM_RELOC_HALF_SECTDIFF: + case ARM_RELOC_HALF: + return true; + default: + return false; + } +} + +/// Trace references from stub atom to lazy pointer to target and get its name. +StringRef ArchHandler_arm::stubName(const DefinedAtom &stubAtom) { + assert(stubAtom.contentType() == DefinedAtom::typeStub); + for (const Reference *ref : stubAtom) { + if (const DefinedAtom* lp = dyn_cast<DefinedAtom>(ref->target())) { + if (lp->contentType() != DefinedAtom::typeLazyPointer) + continue; + for (const Reference *ref2 : *lp) { + if (ref2->kindValue() != lazyPointer) + continue; + return ref2->target()->name(); + } + } + } + return "stub"; +} + +/// Extract displacement from an ARM b/bl/blx instruction. +int32_t ArchHandler_arm::getDisplacementFromArmBranch(uint32_t instruction) { + // Sign-extend imm24 + int32_t displacement = (instruction & 0x00FFFFFF) << 2; + if ((displacement & 0x02000000) != 0) + displacement |= 0xFC000000; + // If this is BLX and H bit set, add 2. + if ((instruction & 0xFF000000) == 0xFB000000) + displacement += 2; + return displacement; +} + +/// Update an ARM b/bl/blx instruction, switching bl <-> blx as needed. +uint32_t ArchHandler_arm::setDisplacementInArmBranch(uint32_t instruction, + int32_t displacement, + bool targetIsThumb) { + assert((displacement <= 33554428) && (displacement > (-33554432)) + && "arm branch out of range"); + bool is_blx = ((instruction & 0xF0000000) == 0xF0000000); + uint32_t newInstruction = (instruction & 0xFF000000); + uint32_t h = 0; + if (targetIsThumb) { + // Force use of BLX. + newInstruction = 0xFA000000; + if (!is_blx) { + assert(((instruction & 0xF0000000) == 0xE0000000) + && "no conditional arm blx"); + assert(((instruction & 0xFF000000) == 0xEB000000) + && "no arm pc-rel BX instruction"); + } + if (displacement & 2) + h = 1; + } + else { + // Force use of B/BL. + if (is_blx) + newInstruction = 0xEB000000; + } + newInstruction |= (h << 24) | ((displacement >> 2) & 0x00FFFFFF); + return newInstruction; +} + +/// Extract displacement from a thumb b/bl/blx instruction. +int32_t ArchHandler_arm::getDisplacementFromThumbBranch(uint32_t instruction, + uint32_t instrAddr) { + bool is_blx = ((instruction & 0xD000F800) == 0xC000F000); + uint32_t s = (instruction >> 10) & 0x1; + uint32_t j1 = (instruction >> 29) & 0x1; + uint32_t j2 = (instruction >> 27) & 0x1; + uint32_t imm10 = instruction & 0x3FF; + uint32_t imm11 = (instruction >> 16) & 0x7FF; + uint32_t i1 = (j1 == s); + uint32_t i2 = (j2 == s); + uint32_t dis = + (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1); + int32_t sdis = dis; + int32_t result = s ? (sdis | 0xFE000000) : sdis; + if (is_blx && (instrAddr & 0x2)) { + // The thumb blx instruction always has low bit of imm11 as zero. The way + // a 2-byte aligned blx can branch to a 4-byte aligned ARM target is that + // the blx instruction always 4-byte aligns the pc before adding the + // displacement from the blx. We must emulate that when decoding this. + result -= 2; + } + return result; +} + +/// Update a thumb b/bl/blx instruction, switching bl <-> blx as needed. +uint32_t ArchHandler_arm::setDisplacementInThumbBranch(uint32_t instruction, + uint32_t instrAddr, + int32_t displacement, + bool targetIsThumb) { + assert((displacement <= 16777214) && (displacement > (-16777216)) + && "thumb branch out of range"); + bool is_bl = ((instruction & 0xD000F800) == 0xD000F000); + bool is_blx = ((instruction & 0xD000F800) == 0xC000F000); + bool is_b = ((instruction & 0xD000F800) == 0x9000F000); + uint32_t newInstruction = (instruction & 0xD000F800); + if (is_bl || is_blx) { + if (targetIsThumb) { + newInstruction = 0xD000F000; // Use bl + } else { + newInstruction = 0xC000F000; // Use blx + // See note in getDisplacementFromThumbBranch() about blx. + if (instrAddr & 0x2) + displacement += 2; + } + } else if (is_b) { + assert(targetIsThumb && "no pc-rel thumb branch instruction that " + "switches to arm mode"); + } + else { + llvm_unreachable("thumb branch22 reloc on a non-branch instruction"); + } + uint32_t s = (uint32_t)(displacement >> 24) & 0x1; + uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1; + uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1; + uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF; + uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF; + uint32_t j1 = (i1 == s); + uint32_t j2 = (i2 == s); + uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11; + uint32_t firstDisp = (s << 10) | imm10; + newInstruction |= (nextDisp << 16) | firstDisp; + return newInstruction; +} + +bool ArchHandler_arm::isThumbMovw(uint32_t instruction) { + return (instruction & 0x8000FBF0) == 0x0000F240; +} + +bool ArchHandler_arm::isThumbMovt(uint32_t instruction) { + return (instruction & 0x8000FBF0) == 0x0000F2C0; +} + +bool ArchHandler_arm::isArmMovw(uint32_t instruction) { + return (instruction & 0x0FF00000) == 0x03000000; +} + +bool ArchHandler_arm::isArmMovt(uint32_t instruction) { + return (instruction & 0x0FF00000) == 0x03400000; +} + + +uint16_t ArchHandler_arm::getWordFromThumbMov(uint32_t instruction) { + assert(isThumbMovw(instruction) || isThumbMovt(instruction)); + uint32_t i = ((instruction & 0x00000400) >> 10); + uint32_t imm4 = (instruction & 0x0000000F); + uint32_t imm3 = ((instruction & 0x70000000) >> 28); + uint32_t imm8 = ((instruction & 0x00FF0000) >> 16); + return (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; +} + +uint16_t ArchHandler_arm::getWordFromArmMov(uint32_t instruction) { + assert(isArmMovw(instruction) || isArmMovt(instruction)); + uint32_t imm4 = ((instruction & 0x000F0000) >> 16); + uint32_t imm12 = (instruction & 0x00000FFF); + return (imm4 << 12) | imm12; +} + + +uint32_t ArchHandler_arm::setWordFromThumbMov(uint32_t instr, uint16_t word) { + assert(isThumbMovw(instr) || isThumbMovt(instr)); + uint32_t imm4 = (word & 0xF000) >> 12; + uint32_t i = (word & 0x0800) >> 11; + uint32_t imm3 = (word & 0x0700) >> 8; + uint32_t imm8 = word & 0x00FF; + return (instr & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16); +} + +uint32_t ArchHandler_arm::setWordFromArmMov(uint32_t instr, uint16_t word) { + assert(isArmMovw(instr) || isArmMovt(instr)); + uint32_t imm4 = (word & 0xF000) >> 12; + uint32_t imm12 = word & 0x0FFF; + return (instr & 0xFFF0F000) | (imm4 << 16) | imm12; +} + + +uint32_t ArchHandler_arm::clearThumbBit(uint32_t value, const Atom *target) { + // The assembler often adds one to the address of a thumb function. + // We need to undo that so it does not look like an addend. + if (value & 1) { + if (isa<DefinedAtom>(target)) { + const MachODefinedAtom *machoTarget = + reinterpret_cast<const MachODefinedAtom *>(target); + if (machoTarget->isThumb()) + value &= -2; // mask off thumb-bit + } + } + return value; +} + +std::error_code ArchHandler_arm::getReferenceInfo( + const Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, + uint64_t fixupAddress, bool isBig, + FindAtomBySectionAndAddress atomFromAddress, + FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, + const lld::Atom **target, Reference::Addend *addend) { + typedef std::error_code E; + const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; + uint64_t targetAddress; + uint32_t instruction = *(const ulittle32_t *)fixupContent; + int32_t displacement; + switch (relocPattern(reloc)) { + case ARM_THUMB_RELOC_BR22 | rPcRel | rExtern | rLength4: + // ex: bl _foo (and _foo is undefined) + if ((instruction & 0xD000F800) == 0x9000F000) + *kind = thumb_b22; + else + *kind = thumb_bl22; + if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + // Instruction contains branch to addend. + displacement = getDisplacementFromThumbBranch(instruction, fixupAddress); + *addend = fixupAddress + 4 + displacement; + return std::error_code(); + case ARM_THUMB_RELOC_BR22 | rPcRel | rLength4: + // ex: bl _foo (and _foo is defined) + if ((instruction & 0xD000F800) == 0x9000F000) + *kind = thumb_b22; + else + *kind = thumb_bl22; + displacement = getDisplacementFromThumbBranch(instruction, fixupAddress); + targetAddress = fixupAddress + 4 + displacement; + return atomFromAddress(reloc.symbol, targetAddress, target, addend); + case ARM_THUMB_RELOC_BR22 | rScattered | rPcRel | rLength4: + // ex: bl _foo+4 (and _foo is defined) + if ((instruction & 0xD000F800) == 0x9000F000) + *kind = thumb_b22; + else + *kind = thumb_bl22; + displacement = getDisplacementFromThumbBranch(instruction, fixupAddress); + targetAddress = fixupAddress + 4 + displacement; + if (E ec = atomFromAddress(0, reloc.value, target, addend)) + return ec; + // reloc.value is target atom's address. Instruction contains branch + // to atom+addend. + *addend += (targetAddress - reloc.value); + return std::error_code(); + case ARM_RELOC_BR24 | rPcRel | rExtern | rLength4: + // ex: bl _foo (and _foo is undefined) + if (((instruction & 0x0F000000) == 0x0A000000) + && ((instruction & 0xF0000000) != 0xF0000000)) + *kind = arm_b24; + else + *kind = arm_bl24; + if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + // Instruction contains branch to addend. + displacement = getDisplacementFromArmBranch(instruction); + *addend = fixupAddress + 8 + displacement; + return std::error_code(); + case ARM_RELOC_BR24 | rPcRel | rLength4: + // ex: bl _foo (and _foo is defined) + if (((instruction & 0x0F000000) == 0x0A000000) + && ((instruction & 0xF0000000) != 0xF0000000)) + *kind = arm_b24; + else + *kind = arm_bl24; + displacement = getDisplacementFromArmBranch(instruction); + targetAddress = fixupAddress + 8 + displacement; + return atomFromAddress(reloc.symbol, targetAddress, target, addend); + case ARM_RELOC_BR24 | rScattered | rPcRel | rLength4: + // ex: bl _foo+4 (and _foo is defined) + if (((instruction & 0x0F000000) == 0x0A000000) + && ((instruction & 0xF0000000) != 0xF0000000)) + *kind = arm_b24; + else + *kind = arm_bl24; + displacement = getDisplacementFromArmBranch(instruction); + targetAddress = fixupAddress + 8 + displacement; + if (E ec = atomFromAddress(0, reloc.value, target, addend)) + return ec; + // reloc.value is target atom's address. Instruction contains branch + // to atom+addend. + *addend += (targetAddress - reloc.value); + return std::error_code(); + case ARM_RELOC_VANILLA | rExtern | rLength4: + // ex: .long _foo (and _foo is undefined) + *kind = pointer32; + if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + *addend = instruction; + return std::error_code(); + case ARM_RELOC_VANILLA | rLength4: + // ex: .long _foo (and _foo is defined) + *kind = pointer32; + if (E ec = atomFromAddress(reloc.symbol, instruction, target, addend)) + return ec; + *addend = clearThumbBit((uint32_t) * addend, *target); + return std::error_code(); + case ARM_RELOC_VANILLA | rScattered | rLength4: + // ex: .long _foo+a (and _foo is defined) + *kind = pointer32; + if (E ec = atomFromAddress(0, reloc.value, target, addend)) + return ec; + *addend += (clearThumbBit(instruction, *target) - reloc.value); + return std::error_code(); + default: + return make_dynamic_error_code(Twine("unsupported arm relocation type")); + } + return std::error_code(); +} + +std::error_code +ArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1, + const normalized::Relocation &reloc2, + const DefinedAtom *inAtom, + uint32_t offsetInAtom, + uint64_t fixupAddress, bool isBig, + bool scatterable, + FindAtomBySectionAndAddress atomFromAddr, + FindAtomBySymbolIndex atomFromSymbolIndex, + Reference::KindValue *kind, + const lld::Atom **target, + Reference::Addend *addend) { + bool pointerDiff = false; + bool funcRel; + bool top; + bool thumbReloc; + switch(relocPattern(reloc1) << 16 | relocPattern(reloc2)) { + case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbLo) << 16 | + ARM_RELOC_PAIR | rScattered | rLenThmbLo): + // ex: movw r1, :lower16:(_x-L1) [thumb mode] + *kind = thumb_movw_funcRel; + funcRel = true; + top = false; + thumbReloc = true; + break; + case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbHi) << 16 | + ARM_RELOC_PAIR | rScattered | rLenThmbHi): + // ex: movt r1, :upper16:(_x-L1) [thumb mode] + *kind = thumb_movt_funcRel; + funcRel = true; + top = true; + thumbReloc = true; + break; + case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmLo) << 16 | + ARM_RELOC_PAIR | rScattered | rLenArmLo): + // ex: movw r1, :lower16:(_x-L1) [arm mode] + *kind = arm_movw_funcRel; + funcRel = true; + top = false; + thumbReloc = false; + break; + case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmHi) << 16 | + ARM_RELOC_PAIR | rScattered | rLenArmHi): + // ex: movt r1, :upper16:(_x-L1) [arm mode] + *kind = arm_movt_funcRel; + funcRel = true; + top = true; + thumbReloc = false; + break; + case ((ARM_RELOC_HALF | rLenThmbLo) << 16 | + ARM_RELOC_PAIR | rLenThmbLo): + // ex: movw r1, :lower16:_x [thumb mode] + *kind = thumb_movw; + funcRel = false; + top = false; + thumbReloc = true; + break; + case ((ARM_RELOC_HALF | rLenThmbHi) << 16 | + ARM_RELOC_PAIR | rLenThmbHi): + // ex: movt r1, :upper16:_x [thumb mode] + *kind = thumb_movt; + funcRel = false; + top = true; + thumbReloc = true; + break; + case ((ARM_RELOC_HALF | rLenArmLo) << 16 | + ARM_RELOC_PAIR | rLenArmLo): + // ex: movw r1, :lower16:_x [arm mode] + *kind = arm_movw; + funcRel = false; + top = false; + thumbReloc = false; + break; + case ((ARM_RELOC_HALF | rLenArmHi) << 16 | + ARM_RELOC_PAIR | rLenArmHi): + // ex: movt r1, :upper16:_x [arm mode] + *kind = arm_movt; + funcRel = false; + top = true; + thumbReloc = false; + break; + case ((ARM_RELOC_HALF | rScattered | rLenThmbLo) << 16 | + ARM_RELOC_PAIR | rLenThmbLo): + // ex: movw r1, :lower16:_x+a [thumb mode] + *kind = thumb_movw; + funcRel = false; + top = false; + thumbReloc = true; + break; + case ((ARM_RELOC_HALF | rScattered | rLenThmbHi) << 16 | + ARM_RELOC_PAIR | rLenThmbHi): + // ex: movt r1, :upper16:_x+a [thumb mode] + *kind = thumb_movt; + funcRel = false; + top = true; + thumbReloc = true; + break; + case ((ARM_RELOC_HALF | rScattered | rLenArmLo) << 16 | + ARM_RELOC_PAIR | rLenArmLo): + // ex: movw r1, :lower16:_x+a [arm mode] + *kind = arm_movw; + funcRel = false; + top = false; + thumbReloc = false; + break; + case ((ARM_RELOC_HALF | rScattered | rLenArmHi) << 16 | + ARM_RELOC_PAIR | rLenArmHi): + // ex: movt r1, :upper16:_x+a [arm mode] + *kind = arm_movt; + funcRel = false; + top = true; + thumbReloc = false; + break; + case ((ARM_RELOC_HALF | rExtern | rLenThmbLo) << 16 | + ARM_RELOC_PAIR | rLenThmbLo): + // ex: movw r1, :lower16:_undef [thumb mode] + *kind = thumb_movw; + funcRel = false; + top = false; + thumbReloc = true; + break; + case ((ARM_RELOC_HALF | rExtern | rLenThmbHi) << 16 | + ARM_RELOC_PAIR | rLenThmbHi): + // ex: movt r1, :upper16:_undef [thumb mode] + *kind = thumb_movt; + funcRel = false; + top = true; + thumbReloc = true; + break; + case ((ARM_RELOC_HALF | rExtern | rLenArmLo) << 16 | + ARM_RELOC_PAIR | rLenArmLo): + // ex: movw r1, :lower16:_undef [arm mode] + *kind = arm_movw; + funcRel = false; + top = false; + thumbReloc = false; + break; + case ((ARM_RELOC_HALF | rExtern | rLenArmHi) << 16 | + ARM_RELOC_PAIR | rLenArmHi): + // ex: movt r1, :upper16:_undef [arm mode] + *kind = arm_movt; + funcRel = false; + top = true; + thumbReloc = false; + break; + case ((ARM_RELOC_SECTDIFF | rScattered | rLength4) << 16 | + ARM_RELOC_PAIR | rScattered | rLength4): + case ((ARM_RELOC_LOCAL_SECTDIFF | rScattered | rLength4) << 16 | + ARM_RELOC_PAIR | rScattered | rLength4): + // ex: .long _foo - . + pointerDiff = true; + break; + default: + return make_dynamic_error_code(Twine("unsupported arm relocation pair")); + } + const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; + std::error_code ec; + uint32_t instruction = *(const ulittle32_t *)fixupContent; + uint32_t value; + uint32_t fromAddress; + uint32_t toAddress; + uint16_t instruction16; + uint16_t other16; + const lld::Atom *fromTarget; + Reference::Addend offsetInTo; + Reference::Addend offsetInFrom; + if (pointerDiff) { + toAddress = reloc1.value; + fromAddress = reloc2.value; + ec = atomFromAddr(0, toAddress, target, &offsetInTo); + if (ec) + return ec; + ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom); + if (ec) + return ec; + if (scatterable && (fromTarget != inAtom)) + return make_dynamic_error_code(Twine("SECTDIFF relocation where " + "subtrahend label is not in atom")); + *kind = delta32; + value = clearThumbBit(instruction, *target); + *addend = (int32_t)(value - (toAddress - fixupAddress)); + } else if (funcRel) { + toAddress = reloc1.value; + fromAddress = reloc2.value; + ec = atomFromAddr(0, toAddress, target, &offsetInTo); + if (ec) + return ec; + ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom); + if (ec) + return ec; + if (fromTarget != inAtom) + return make_dynamic_error_code( + Twine("ARM_RELOC_HALF_SECTDIFF relocation " + "where subtrahend label is not in atom")); + other16 = (reloc2.offset & 0xFFFF); + if (thumbReloc) { + if (top) { + if (!isThumbMovt(instruction)) + return make_dynamic_error_code(Twine("expected movt instruction")); + } + else { + if (!isThumbMovw(instruction)) + return make_dynamic_error_code(Twine("expected movw instruction")); + } + instruction16 = getWordFromThumbMov(instruction); + } + else { + if (top) { + if (!isArmMovt(instruction)) + return make_dynamic_error_code(Twine("expected movt instruction")); + } + else { + if (!isArmMovw(instruction)) + return make_dynamic_error_code(Twine("expected movw instruction")); + } + instruction16 = getWordFromArmMov(instruction); + } + if (top) + value = (instruction16 << 16) | other16; + else + value = (other16 << 16) | instruction16; + value = clearThumbBit(value, *target); + int64_t ta = (int64_t) value - (toAddress - fromAddress); + *addend = ta - offsetInFrom; + return std::error_code(); + } else { + uint32_t sectIndex; + if (thumbReloc) { + if (top) { + if (!isThumbMovt(instruction)) + return make_dynamic_error_code(Twine("expected movt instruction")); + } + else { + if (!isThumbMovw(instruction)) + return make_dynamic_error_code(Twine("expected movw instruction")); + } + instruction16 = getWordFromThumbMov(instruction); + } + else { + if (top) { + if (!isArmMovt(instruction)) + return make_dynamic_error_code(Twine("expected movt instruction")); + } + else { + if (!isArmMovw(instruction)) + return make_dynamic_error_code(Twine("expected movw instruction")); + } + instruction16 = getWordFromArmMov(instruction); + } + other16 = (reloc2.offset & 0xFFFF); + if (top) + value = (instruction16 << 16) | other16; + else + value = (other16 << 16) | instruction16; + if (reloc1.isExtern) { + ec = atomFromSymbolIndex(reloc1.symbol, target); + if (ec) + return ec; + *addend = value; + } else { + if (reloc1.scattered) { + toAddress = reloc1.value; + sectIndex = 0; + } else { + toAddress = value; + sectIndex = reloc1.symbol; + } + ec = atomFromAddr(sectIndex, toAddress, target, &offsetInTo); + if (ec) + return ec; + *addend = value - toAddress; + } + } + + return std::error_code(); +} + +void ArchHandler_arm::applyFixupFinal(const Reference &ref, uint8_t *loc, + uint64_t fixupAddress, + uint64_t targetAddress, + uint64_t inAtomAddress, + bool &thumbMode, bool targetIsThumb) { + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) + return; + assert(ref.kindArch() == Reference::KindArch::ARM); + ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc); + int32_t displacement; + uint16_t value16; + uint32_t value32; + switch (static_cast<ArmKind>(ref.kindValue())) { + case modeThumbCode: + thumbMode = true; + break; + case modeArmCode: + thumbMode = false; + break; + case modeData: + break; + case thumb_b22: + case thumb_bl22: + assert(thumbMode); + displacement = (targetAddress - (fixupAddress + 4)) + ref.addend(); + value32 = setDisplacementInThumbBranch(*loc32, fixupAddress, + displacement, targetIsThumb); + *loc32 = value32; + break; + case thumb_movw: + assert(thumbMode); + value16 = (targetAddress + ref.addend()) & 0xFFFF; + if (targetIsThumb) + value16 |= 1; + *loc32 = setWordFromThumbMov(*loc32, value16); + break; + case thumb_movt: + assert(thumbMode); + value16 = (targetAddress + ref.addend()) >> 16; + *loc32 = setWordFromThumbMov(*loc32, value16); + break; + case thumb_movw_funcRel: + assert(thumbMode); + value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF; + if (targetIsThumb) + value16 |= 1; + *loc32 = setWordFromThumbMov(*loc32, value16); + break; + case thumb_movt_funcRel: + assert(thumbMode); + value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16; + *loc32 = setWordFromThumbMov(*loc32, value16); + break; + case arm_b24: + case arm_bl24: + assert(!thumbMode); + displacement = (targetAddress - (fixupAddress + 8)) + ref.addend(); + value32 = setDisplacementInArmBranch(*loc32, displacement, targetIsThumb); + *loc32 = value32; + break; + case arm_movw: + assert(!thumbMode); + value16 = (targetAddress + ref.addend()) & 0xFFFF; + if (targetIsThumb) + value16 |= 1; + *loc32 = setWordFromArmMov(*loc32, value16); + break; + case arm_movt: + assert(!thumbMode); + value16 = (targetAddress + ref.addend()) >> 16; + *loc32 = setWordFromArmMov(*loc32, value16); + break; + case arm_movw_funcRel: + assert(!thumbMode); + value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF; + if (targetIsThumb) + value16 |= 1; + *loc32 = setWordFromArmMov(*loc32, value16); + break; + case arm_movt_funcRel: + assert(!thumbMode); + value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16; + *loc32 = setWordFromArmMov(*loc32, value16); + break; + case pointer32: + if (targetIsThumb) + *loc32 = targetAddress + ref.addend() + 1; + else + *loc32 = targetAddress + ref.addend(); + break; + case delta32: + if (targetIsThumb) + *loc32 = targetAddress - fixupAddress + ref.addend() + 1; + else + *loc32 = targetAddress - fixupAddress + ref.addend(); + break; + case lazyPointer: + // do nothing + break; + case lazyImmediateLocation: + *loc32 = ref.addend(); + break; + case invalid: + llvm_unreachable("invalid ARM Reference Kind"); + break; + } +} + +void ArchHandler_arm::generateAtomContent(const DefinedAtom &atom, + bool relocatable, + FindAddressForAtom findAddress, + FindAddressForAtom findSectionAddress, + uint64_t imageBaseAddress, + uint8_t *atomContentBuffer) { + // Copy raw bytes. + memcpy(atomContentBuffer, atom.rawContent().data(), atom.size()); + // Apply fix-ups. + bool thumbMode = false; + for (const Reference *ref : atom) { + uint32_t offset = ref->offsetInAtom(); + const Atom *target = ref->target(); + uint64_t targetAddress = 0; + bool targetIsThumb = false; + if (const DefinedAtom *defTarg = dyn_cast<DefinedAtom>(target)) { + targetAddress = findAddress(*target); + targetIsThumb = isThumbFunction(*defTarg); + } + uint64_t atomAddress = findAddress(atom); + uint64_t fixupAddress = atomAddress + offset; + if (relocatable) { + applyFixupRelocatable(*ref, &atomContentBuffer[offset], fixupAddress, + targetAddress, atomAddress, thumbMode, + targetIsThumb); + } else { + applyFixupFinal(*ref, &atomContentBuffer[offset], fixupAddress, + targetAddress, atomAddress, thumbMode, targetIsThumb); + } + } +} + + +bool ArchHandler_arm::useExternalRelocationTo(const Atom &target) { + // Undefined symbols are referenced via external relocations. + if (isa<UndefinedAtom>(&target)) + return true; + if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(&target)) { + switch (defAtom->merge()) { + case DefinedAtom::mergeAsTentative: + // Tentative definitions are referenced via external relocations. + return true; + case DefinedAtom::mergeAsWeak: + case DefinedAtom::mergeAsWeakAndAddressUsed: + // Global weak-defs are referenced via external relocations. + return (defAtom->scope() == DefinedAtom::scopeGlobal); + default: + break; + } + } + // Everything else is reference via an internal relocation. + return false; +} + +void ArchHandler_arm::applyFixupRelocatable(const Reference &ref, uint8_t *loc, + uint64_t fixupAddress, + uint64_t targetAddress, + uint64_t inAtomAddress, + bool &thumbMode, + bool targetIsThumb) { + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) + return; + assert(ref.kindArch() == Reference::KindArch::ARM); + bool useExternalReloc = useExternalRelocationTo(*ref.target()); + ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc); + int32_t displacement; + uint16_t value16; + uint32_t value32; + bool targetIsUndef = isa<UndefinedAtom>(ref.target()); + switch (static_cast<ArmKind>(ref.kindValue())) { + case modeThumbCode: + thumbMode = true; + break; + case modeArmCode: + thumbMode = false; + break; + case modeData: + break; + case thumb_b22: + case thumb_bl22: + assert(thumbMode); + if (useExternalReloc) + displacement = (ref.addend() - (fixupAddress + 4)); + else + displacement = (targetAddress - (fixupAddress + 4)) + ref.addend(); + value32 = setDisplacementInThumbBranch(*loc32, fixupAddress, + displacement, + targetIsUndef || targetIsThumb); + *loc32 = value32; + break; + case thumb_movw: + assert(thumbMode); + if (useExternalReloc) + value16 = ref.addend() & 0xFFFF; + else + value16 = (targetAddress + ref.addend()) & 0xFFFF; + *loc32 = setWordFromThumbMov(*loc32, value16); + break; + case thumb_movt: + assert(thumbMode); + if (useExternalReloc) + value16 = ref.addend() >> 16; + else + value16 = (targetAddress + ref.addend()) >> 16; + *loc32 = setWordFromThumbMov(*loc32, value16); + break; + case thumb_movw_funcRel: + assert(thumbMode); + value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF; + *loc32 = setWordFromThumbMov(*loc32, value16); + break; + case thumb_movt_funcRel: + assert(thumbMode); + value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16; + *loc32 = setWordFromThumbMov(*loc32, value16); + break; + case arm_b24: + case arm_bl24: + assert(!thumbMode); + if (useExternalReloc) + displacement = (ref.addend() - (fixupAddress + 8)); + else + displacement = (targetAddress - (fixupAddress + 8)) + ref.addend(); + value32 = setDisplacementInArmBranch(*loc32, displacement, + targetIsThumb); + *loc32 = value32; + break; + case arm_movw: + assert(!thumbMode); + if (useExternalReloc) + value16 = ref.addend() & 0xFFFF; + else + value16 = (targetAddress + ref.addend()) & 0xFFFF; + *loc32 = setWordFromArmMov(*loc32, value16); + break; + case arm_movt: + assert(!thumbMode); + if (useExternalReloc) + value16 = ref.addend() >> 16; + else + value16 = (targetAddress + ref.addend()) >> 16; + *loc32 = setWordFromArmMov(*loc32, value16); + break; + case arm_movw_funcRel: + assert(!thumbMode); + value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF; + *loc32 = setWordFromArmMov(*loc32, value16); + break; + case arm_movt_funcRel: + assert(!thumbMode); + value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16; + *loc32 = setWordFromArmMov(*loc32, value16); + break; + case pointer32: + *loc32 = targetAddress + ref.addend(); + break; + case delta32: + *loc32 = targetAddress - fixupAddress + ref.addend(); + break; + case lazyPointer: + case lazyImmediateLocation: + // do nothing + break; + case invalid: + llvm_unreachable("invalid ARM Reference Kind"); + break; + } +} + +void ArchHandler_arm::appendSectionRelocations( + const DefinedAtom &atom, + uint64_t atomSectionOffset, + const Reference &ref, + FindSymbolIndexForAtom symbolIndexForAtom, + FindSectionIndexForAtom sectionIndexForAtom, + FindAddressForAtom addressForAtom, + normalized::Relocations &relocs) { + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) + return; + assert(ref.kindArch() == Reference::KindArch::ARM); + uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom(); + bool useExternalReloc = useExternalRelocationTo(*ref.target()); + uint32_t targetAtomAddress; + uint32_t fromAtomAddress; + uint16_t other16; + switch (static_cast<ArmKind>(ref.kindValue())) { + case modeThumbCode: + case modeArmCode: + case modeData: + // Do nothing. + break; + case thumb_b22: + case thumb_bl22: + if (useExternalReloc) { + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM_THUMB_RELOC_BR22 | rExtern | rPcRel | rLength4); + } else { + if (ref.addend() != 0) + appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), + ARM_THUMB_RELOC_BR22 | rScattered | rPcRel | rLength4); + else + appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, + ARM_THUMB_RELOC_BR22 | rPcRel | rLength4); + } + break; + case thumb_movw: + if (useExternalReloc) { + other16 = ref.addend() >> 16; + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM_RELOC_HALF | rExtern | rLenThmbLo); + appendReloc(relocs, other16, 0, 0, + ARM_RELOC_PAIR | rLenThmbLo); + } else { + targetAtomAddress = addressForAtom(*ref.target()); + if (ref.addend() != 0) { + other16 = (targetAtomAddress + ref.addend()) >> 16; + appendReloc(relocs, sectionOffset, 0, targetAtomAddress, + ARM_RELOC_HALF | rScattered | rLenThmbLo); + appendReloc(relocs, other16, 0, 0, + ARM_RELOC_PAIR | rLenThmbLo); + } else { + other16 = (targetAtomAddress + ref.addend()) >> 16; + appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, + ARM_RELOC_HALF | rLenThmbLo); + appendReloc(relocs, other16, 0, 0, + ARM_RELOC_PAIR | rLenThmbLo); + } + } + break; + case thumb_movt: + if (useExternalReloc) { + other16 = ref.addend() & 0xFFFF; + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM_RELOC_HALF | rExtern | rLenThmbHi); + appendReloc(relocs, other16, 0, 0, + ARM_RELOC_PAIR | rLenThmbHi); + } else { + targetAtomAddress = addressForAtom(*ref.target()); + if (ref.addend() != 0) { + other16 = (targetAtomAddress + ref.addend()) & 0xFFFF; + appendReloc(relocs, sectionOffset, 0, targetAtomAddress, + ARM_RELOC_HALF | rScattered | rLenThmbHi); + appendReloc(relocs, other16, 0, 0, + ARM_RELOC_PAIR | rLenThmbHi); + } else { + other16 = (targetAtomAddress + ref.addend()) & 0xFFFF; + appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, + ARM_RELOC_HALF | rLenThmbHi); + appendReloc(relocs, other16, 0, 0, + ARM_RELOC_PAIR | rLenThmbHi); + } + } + break; + case thumb_movw_funcRel: + fromAtomAddress = addressForAtom(atom); + targetAtomAddress = addressForAtom(*ref.target()); + other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) >> 16; + appendReloc(relocs, sectionOffset, 0, targetAtomAddress, + ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbLo); + appendReloc(relocs, other16, 0, fromAtomAddress, + ARM_RELOC_PAIR | rScattered | rLenThmbLo); + break; + case thumb_movt_funcRel: + fromAtomAddress = addressForAtom(atom); + targetAtomAddress = addressForAtom(*ref.target()); + other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) & 0xFFFF; + appendReloc(relocs, sectionOffset, 0, targetAtomAddress, + ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbHi); + appendReloc(relocs, other16, 0, fromAtomAddress, + ARM_RELOC_PAIR | rScattered | rLenThmbHi); + break; + case arm_b24: + case arm_bl24: + if (useExternalReloc) { + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM_RELOC_BR24 | rExtern | rPcRel | rLength4); + } else { + if (ref.addend() != 0) + appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), + ARM_RELOC_BR24 | rScattered | rPcRel | rLength4); + else + appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, + ARM_RELOC_BR24 | rPcRel | rLength4); + } + break; + case arm_movw: + if (useExternalReloc) { + other16 = ref.addend() >> 16; + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM_RELOC_HALF | rExtern | rLenArmLo); + appendReloc(relocs, other16, 0, 0, + ARM_RELOC_PAIR | rLenArmLo); + } else { + targetAtomAddress = addressForAtom(*ref.target()); + if (ref.addend() != 0) { + other16 = (targetAtomAddress + ref.addend()) >> 16; + appendReloc(relocs, sectionOffset, 0, targetAtomAddress, + ARM_RELOC_HALF | rScattered | rLenArmLo); + appendReloc(relocs, other16, 0, 0, + ARM_RELOC_PAIR | rLenArmLo); + } else { + other16 = (targetAtomAddress + ref.addend()) >> 16; + appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, + ARM_RELOC_HALF | rLenArmLo); + appendReloc(relocs, other16, 0, 0, + ARM_RELOC_PAIR | rLenArmLo); + } + } + break; + case arm_movt: + if (useExternalReloc) { + other16 = ref.addend() & 0xFFFF; + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM_RELOC_HALF | rExtern | rLenArmHi); + appendReloc(relocs, other16, 0, 0, + ARM_RELOC_PAIR | rLenArmHi); + } else { + targetAtomAddress = addressForAtom(*ref.target()); + if (ref.addend() != 0) { + other16 = (targetAtomAddress + ref.addend()) & 0xFFFF; + appendReloc(relocs, sectionOffset, 0, targetAtomAddress, + ARM_RELOC_HALF | rScattered | rLenArmHi); + appendReloc(relocs, other16, 0, 0, + ARM_RELOC_PAIR | rLenArmHi); + } else { + other16 = (targetAtomAddress + ref.addend()) & 0xFFFF; + appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, + ARM_RELOC_HALF | rLenArmHi); + appendReloc(relocs, other16, 0, 0, + ARM_RELOC_PAIR | rLenArmHi); + } + } + break; + case arm_movw_funcRel: + fromAtomAddress = addressForAtom(atom); + targetAtomAddress = addressForAtom(*ref.target()); + other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) >> 16; + appendReloc(relocs, sectionOffset, 0, targetAtomAddress, + ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmLo); + appendReloc(relocs, other16, 0, fromAtomAddress, + ARM_RELOC_PAIR | rScattered | rLenArmLo); + break; + case arm_movt_funcRel: + fromAtomAddress = addressForAtom(atom); + targetAtomAddress = addressForAtom(*ref.target()); + other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) & 0xFFFF; + appendReloc(relocs, sectionOffset, 0, targetAtomAddress, + ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmHi); + appendReloc(relocs, other16, 0, fromAtomAddress, + ARM_RELOC_PAIR | rScattered | rLenArmHi); + break; + case pointer32: + if (useExternalReloc) { + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM_RELOC_VANILLA | rExtern | rLength4); + } + else { + if (ref.addend() != 0) + appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), + ARM_RELOC_VANILLA | rScattered | rLength4); + else + appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, + ARM_RELOC_VANILLA | rLength4); + } + break; + case delta32: + appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), + ARM_RELOC_SECTDIFF | rScattered | rLength4); + appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) + + ref.offsetInAtom(), + ARM_RELOC_PAIR | rScattered | rLength4); + break; + case lazyPointer: + case lazyImmediateLocation: + // do nothing + break; + case invalid: + llvm_unreachable("invalid ARM Reference Kind"); + break; + } +} + +void ArchHandler_arm::addAdditionalReferences(MachODefinedAtom &atom) { + if (atom.isThumb()) { + atom.addReference(0, modeThumbCode, &atom, 0, Reference::KindArch::ARM); + } +} + +bool ArchHandler_arm::isThumbFunction(const DefinedAtom &atom) { + for (const Reference *ref : atom) { + if (ref->offsetInAtom() != 0) + return false; + if (ref->kindNamespace() != Reference::KindNamespace::mach_o) + continue; + assert(ref->kindArch() == Reference::KindArch::ARM); + if (ref->kindValue() == modeThumbCode) + return true; + } + return false; +} + + +class Thumb2ToArmShimAtom : public SimpleDefinedAtom { +public: + Thumb2ToArmShimAtom(MachOFile &file, StringRef targetName, + const DefinedAtom &target) + : SimpleDefinedAtom(file) { + addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM, + ArchHandler_arm::modeThumbCode, 0, this, 0); + addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM, + ArchHandler_arm::delta32, 8, &target, 0); + std::string name = std::string(targetName) + "$shim"; + StringRef tmp(name); + _name = tmp.copy(file.allocator()); + } + + StringRef name() const override { + return _name; + } + + ContentType contentType() const override { + return DefinedAtom::typeCode; + } + + Alignment alignment() const override { + return Alignment(2); + } + + uint64_t size() const override { + return 12; + } + + ContentPermissions permissions() const override { + return DefinedAtom::permR_X; + } + + ArrayRef<uint8_t> rawContent() const override { + static const uint8_t bytes[] = + { 0xDF, 0xF8, 0x04, 0xC0, // ldr ip, pc + 4 + 0xFF, 0x44, // add ip, pc, ip + 0x60, 0x47, // ldr pc, [ip] + 0x00, 0x00, 0x00, 0x00 }; // .long target - this + assert(sizeof(bytes) == size()); + return llvm::makeArrayRef(bytes, sizeof(bytes)); + } +private: + StringRef _name; +}; + + +class ArmToThumbShimAtom : public SimpleDefinedAtom { +public: + ArmToThumbShimAtom(MachOFile &file, StringRef targetName, + const DefinedAtom &target) + : SimpleDefinedAtom(file) { + addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM, + ArchHandler_arm::delta32, 12, &target, 0); + std::string name = std::string(targetName) + "$shim"; + StringRef tmp(name); + _name = tmp.copy(file.allocator()); + } + + StringRef name() const override { + return _name; + } + + ContentType contentType() const override { + return DefinedAtom::typeCode; + } + + Alignment alignment() const override { + return Alignment(2); + } + + uint64_t size() const override { + return 16; + } + + ContentPermissions permissions() const override { + return DefinedAtom::permR_X; + } + + ArrayRef<uint8_t> rawContent() const override { + static const uint8_t bytes[] = + { 0x04, 0xC0, 0x9F, 0xE5, // ldr ip, pc + 4 + 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip + 0x1C, 0xFF, 0x2F, 0xE1, // ldr pc, [ip] + 0x00, 0x00, 0x00, 0x00 }; // .long target - this + assert(sizeof(bytes) == size()); + return llvm::makeArrayRef(bytes, sizeof(bytes)); + } +private: + StringRef _name; +}; + +const DefinedAtom *ArchHandler_arm::createShim(MachOFile &file, + bool thumbToArm, + const DefinedAtom &target) { + bool isStub = (target.contentType() == DefinedAtom::typeStub); + StringRef targetName = isStub ? stubName(target) : target.name(); + if (thumbToArm) + return new (file.allocator()) Thumb2ToArmShimAtom(file, targetName, target); + else + return new (file.allocator()) ArmToThumbShimAtom(file, targetName, target); +} + + +std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_arm() { + return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_arm()); +} + +} // namespace mach_o +} // namespace lld diff --git a/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp b/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp new file mode 100644 index 0000000000000..fd9984b89ce6e --- /dev/null +++ b/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp @@ -0,0 +1,822 @@ +//===- lib/FileFormat/MachO/ArchHandler_arm64.cpp -------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ArchHandler.h" +#include "Atoms.h" +#include "MachONormalizedFileBinaryUtils.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" + +using namespace llvm::MachO; +using namespace lld::mach_o::normalized; + +namespace lld { +namespace mach_o { + +using llvm::support::ulittle32_t; +using llvm::support::ulittle64_t; + +using llvm::support::little32_t; +using llvm::support::little64_t; + +class ArchHandler_arm64 : public ArchHandler { +public: + ArchHandler_arm64(); + virtual ~ArchHandler_arm64(); + + const Registry::KindStrings *kindStrings() override { return _sKindStrings; } + + Reference::KindArch kindArch() override { + return Reference::KindArch::AArch64; + } + + /// Used by GOTPass to locate GOT References + bool isGOTAccess(const Reference &ref, bool &canBypassGOT) override { + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) + return false; + assert(ref.kindArch() == Reference::KindArch::AArch64); + switch (ref.kindValue()) { + case gotPage21: + case gotOffset12: + canBypassGOT = true; + return true; + case imageOffsetGot: + canBypassGOT = false; + return true; + default: + return false; + } + } + + /// Used by GOTPass to update GOT References. + void updateReferenceToGOT(const Reference *ref, bool targetNowGOT) override { + // If GOT slot was instanciated, transform: + // gotPage21/gotOffset12 -> page21/offset12scale8 + // If GOT slot optimized away, transform: + // gotPage21/gotOffset12 -> page21/addOffset12 + assert(ref->kindNamespace() == Reference::KindNamespace::mach_o); + assert(ref->kindArch() == Reference::KindArch::AArch64); + switch (ref->kindValue()) { + case gotPage21: + const_cast<Reference *>(ref)->setKindValue(page21); + break; + case gotOffset12: + const_cast<Reference *>(ref)->setKindValue(targetNowGOT ? + offset12scale8 : addOffset12); + break; + case imageOffsetGot: + const_cast<Reference *>(ref)->setKindValue(imageOffset); + break; + default: + llvm_unreachable("Not a GOT reference"); + } + } + + const StubInfo &stubInfo() override { return _sStubInfo; } + + bool isCallSite(const Reference &) override; + bool isNonCallBranch(const Reference &) override { + return false; + } + + bool isPointer(const Reference &) override; + bool isPairedReloc(const normalized::Relocation &) override; + + bool needsCompactUnwind() override { + return true; + } + Reference::KindValue imageOffsetKind() override { + return imageOffset; + } + Reference::KindValue imageOffsetKindIndirect() override { + return imageOffsetGot; + } + + Reference::KindValue unwindRefToCIEKind() override { + return negDelta32; + } + + Reference::KindValue unwindRefToFunctionKind() override { + return unwindFDEToFunction; + } + + Reference::KindValue unwindRefToEhFrameKind() override { + return unwindInfoToEhFrame; + } + + uint32_t dwarfCompactUnwindType() override { + return 0x03000000; + } + + std::error_code getReferenceInfo(const normalized::Relocation &reloc, + const DefinedAtom *inAtom, + uint32_t offsetInAtom, + uint64_t fixupAddress, bool isBig, + FindAtomBySectionAndAddress atomFromAddress, + FindAtomBySymbolIndex atomFromSymbolIndex, + Reference::KindValue *kind, + const lld::Atom **target, + Reference::Addend *addend) override; + std::error_code + getPairReferenceInfo(const normalized::Relocation &reloc1, + const normalized::Relocation &reloc2, + const DefinedAtom *inAtom, + uint32_t offsetInAtom, + uint64_t fixupAddress, bool isBig, bool scatterable, + FindAtomBySectionAndAddress atomFromAddress, + FindAtomBySymbolIndex atomFromSymbolIndex, + Reference::KindValue *kind, + const lld::Atom **target, + Reference::Addend *addend) override; + + bool needsLocalSymbolInRelocatableFile(const DefinedAtom *atom) override { + return (atom->contentType() == DefinedAtom::typeCString); + } + + void generateAtomContent(const DefinedAtom &atom, bool relocatable, + FindAddressForAtom findAddress, + FindAddressForAtom findSectionAddress, + uint64_t imageBaseAddress, + uint8_t *atomContentBuffer) override; + + void appendSectionRelocations(const DefinedAtom &atom, + uint64_t atomSectionOffset, + const Reference &ref, + FindSymbolIndexForAtom symbolIndexForAtom, + FindSectionIndexForAtom sectionIndexForAtom, + FindAddressForAtom addressForAtom, + normalized::Relocations &relocs) override; + +private: + static const Registry::KindStrings _sKindStrings[]; + static const StubInfo _sStubInfo; + + enum Arm64Kind : Reference::KindValue { + invalid, /// for error condition + + // Kinds found in mach-o .o files: + branch26, /// ex: bl _foo + page21, /// ex: adrp x1, _foo@PAGE + offset12, /// ex: ldrb w0, [x1, _foo@PAGEOFF] + offset12scale2, /// ex: ldrs w0, [x1, _foo@PAGEOFF] + offset12scale4, /// ex: ldr w0, [x1, _foo@PAGEOFF] + offset12scale8, /// ex: ldr x0, [x1, _foo@PAGEOFF] + offset12scale16, /// ex: ldr q0, [x1, _foo@PAGEOFF] + gotPage21, /// ex: adrp x1, _foo@GOTPAGE + gotOffset12, /// ex: ldr w0, [x1, _foo@GOTPAGEOFF] + tlvPage21, /// ex: adrp x1, _foo@TLVPAGE + tlvOffset12, /// ex: ldr w0, [x1, _foo@TLVPAGEOFF] + + pointer64, /// ex: .quad _foo + delta64, /// ex: .quad _foo - . + delta32, /// ex: .long _foo - . + negDelta32, /// ex: .long . - _foo + pointer64ToGOT, /// ex: .quad _foo@GOT + delta32ToGOT, /// ex: .long _foo@GOT - . + + // Kinds introduced by Passes: + addOffset12, /// Location contains LDR to change into ADD. + lazyPointer, /// Location contains a lazy pointer. + lazyImmediateLocation, /// Location contains immediate value used in stub. + imageOffset, /// Location contains offset of atom in final image + imageOffsetGot, /// Location contains offset of GOT entry for atom in + /// final image (typically personality function). + unwindFDEToFunction, /// Nearly delta64, but cannot be rematerialized in + /// relocatable object (yay for implicit contracts!). + unwindInfoToEhFrame, /// Fix low 24 bits of compact unwind encoding to + /// refer to __eh_frame entry. + }; + + void applyFixupFinal(const Reference &ref, uint8_t *location, + uint64_t fixupAddress, uint64_t targetAddress, + uint64_t inAtomAddress, uint64_t imageBaseAddress, + FindAddressForAtom findSectionAddress); + + void applyFixupRelocatable(const Reference &ref, uint8_t *location, + uint64_t fixupAddress, uint64_t targetAddress, + uint64_t inAtomAddress, bool targetUnnamed); + + // Utility functions for inspecting/updating instructions. + static uint32_t setDisplacementInBranch26(uint32_t instr, int32_t disp); + static uint32_t setDisplacementInADRP(uint32_t instr, int64_t disp); + static Arm64Kind offset12KindFromInstruction(uint32_t instr); + static uint32_t setImm12(uint32_t instr, uint32_t offset); +}; + +ArchHandler_arm64::ArchHandler_arm64() {} + +ArchHandler_arm64::~ArchHandler_arm64() {} + +const Registry::KindStrings ArchHandler_arm64::_sKindStrings[] = { + LLD_KIND_STRING_ENTRY(invalid), + LLD_KIND_STRING_ENTRY(branch26), + LLD_KIND_STRING_ENTRY(page21), + LLD_KIND_STRING_ENTRY(offset12), + LLD_KIND_STRING_ENTRY(offset12scale2), + LLD_KIND_STRING_ENTRY(offset12scale4), + LLD_KIND_STRING_ENTRY(offset12scale8), + LLD_KIND_STRING_ENTRY(offset12scale16), + LLD_KIND_STRING_ENTRY(gotPage21), + LLD_KIND_STRING_ENTRY(gotOffset12), + LLD_KIND_STRING_ENTRY(tlvPage21), + LLD_KIND_STRING_ENTRY(tlvOffset12), + LLD_KIND_STRING_ENTRY(pointer64), + LLD_KIND_STRING_ENTRY(delta64), + LLD_KIND_STRING_ENTRY(delta32), + LLD_KIND_STRING_ENTRY(negDelta32), + LLD_KIND_STRING_ENTRY(pointer64ToGOT), + LLD_KIND_STRING_ENTRY(delta32ToGOT), + + LLD_KIND_STRING_ENTRY(addOffset12), + LLD_KIND_STRING_ENTRY(lazyPointer), + LLD_KIND_STRING_ENTRY(lazyImmediateLocation), + LLD_KIND_STRING_ENTRY(imageOffset), + LLD_KIND_STRING_ENTRY(imageOffsetGot), + LLD_KIND_STRING_ENTRY(unwindFDEToFunction), + LLD_KIND_STRING_ENTRY(unwindInfoToEhFrame), + + LLD_KIND_STRING_END +}; + +const ArchHandler::StubInfo ArchHandler_arm64::_sStubInfo = { + "dyld_stub_binder", + + // Lazy pointer references + { Reference::KindArch::AArch64, pointer64, 0, 0 }, + { Reference::KindArch::AArch64, lazyPointer, 0, 0 }, + + // GOT pointer to dyld_stub_binder + { Reference::KindArch::AArch64, pointer64, 0, 0 }, + + // arm64 code alignment 2^2 + 2, + + // Stub size and code + 12, + { 0x10, 0x00, 0x00, 0x90, // ADRP X16, lazy_pointer@page + 0x10, 0x02, 0x40, 0xF9, // LDR X16, [X16, lazy_pointer@pageoff] + 0x00, 0x02, 0x1F, 0xD6 }, // BR X16 + { Reference::KindArch::AArch64, page21, 0, 0 }, + { true, offset12scale8, 4, 0 }, + + // Stub Helper size and code + 12, + { 0x50, 0x00, 0x00, 0x18, // LDR W16, L0 + 0x00, 0x00, 0x00, 0x14, // LDR B helperhelper + 0x00, 0x00, 0x00, 0x00 }, // L0: .long 0 + { Reference::KindArch::AArch64, lazyImmediateLocation, 8, 0 }, + { Reference::KindArch::AArch64, branch26, 4, 0 }, + + // Stub Helper-Common size and code + 24, + { 0x11, 0x00, 0x00, 0x90, // ADRP X17, dyld_ImageLoaderCache@page + 0x31, 0x02, 0x00, 0x91, // ADD X17, X17, dyld_ImageLoaderCache@pageoff + 0xF0, 0x47, 0xBF, 0xA9, // STP X16/X17, [SP, #-16]! + 0x10, 0x00, 0x00, 0x90, // ADRP X16, _fast_lazy_bind@page + 0x10, 0x02, 0x40, 0xF9, // LDR X16, [X16,_fast_lazy_bind@pageoff] + 0x00, 0x02, 0x1F, 0xD6 }, // BR X16 + { Reference::KindArch::AArch64, page21, 0, 0 }, + { true, offset12, 4, 0 }, + { Reference::KindArch::AArch64, page21, 12, 0 }, + { true, offset12scale8, 16, 0 } +}; + +bool ArchHandler_arm64::isCallSite(const Reference &ref) { + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) + return false; + assert(ref.kindArch() == Reference::KindArch::AArch64); + return (ref.kindValue() == branch26); +} + +bool ArchHandler_arm64::isPointer(const Reference &ref) { + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) + return false; + assert(ref.kindArch() == Reference::KindArch::AArch64); + Reference::KindValue kind = ref.kindValue(); + return (kind == pointer64); +} + +bool ArchHandler_arm64::isPairedReloc(const Relocation &r) { + return ((r.type == ARM64_RELOC_ADDEND) || (r.type == ARM64_RELOC_SUBTRACTOR)); +} + +uint32_t ArchHandler_arm64::setDisplacementInBranch26(uint32_t instr, + int32_t displacement) { + assert((displacement <= 134217727) && (displacement > (-134217728)) && + "arm64 branch out of range"); + return (instr & 0xFC000000) | ((uint32_t)(displacement >> 2) & 0x03FFFFFF); +} + +uint32_t ArchHandler_arm64::setDisplacementInADRP(uint32_t instruction, + int64_t displacement) { + assert((displacement <= 0x100000000LL) && (displacement > (-0x100000000LL)) && + "arm64 ADRP out of range"); + assert(((instruction & 0x9F000000) == 0x90000000) && + "reloc not on ADRP instruction"); + uint32_t immhi = (displacement >> 9) & (0x00FFFFE0); + uint32_t immlo = (displacement << 17) & (0x60000000); + return (instruction & 0x9F00001F) | immlo | immhi; +} + +ArchHandler_arm64::Arm64Kind +ArchHandler_arm64::offset12KindFromInstruction(uint32_t instruction) { + if (instruction & 0x08000000) { + switch ((instruction >> 30) & 0x3) { + case 0: + if ((instruction & 0x04800000) == 0x04800000) + return offset12scale16; + return offset12; + case 1: + return offset12scale2; + case 2: + return offset12scale4; + case 3: + return offset12scale8; + } + } + return offset12; +} + +uint32_t ArchHandler_arm64::setImm12(uint32_t instruction, uint32_t offset) { + assert(((offset & 0xFFFFF000) == 0) && "imm12 offset out of range"); + uint32_t imm12 = offset << 10; + return (instruction & 0xFFC003FF) | imm12; +} + +std::error_code ArchHandler_arm64::getReferenceInfo( + const Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, + uint64_t fixupAddress, bool isBig, + FindAtomBySectionAndAddress atomFromAddress, + FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, + const lld::Atom **target, Reference::Addend *addend) { + const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; + switch (relocPattern(reloc)) { + case ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4: + // ex: bl _foo + *kind = branch26; + if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + *addend = 0; + return std::error_code(); + case ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4: + // ex: adrp x1, _foo@PAGE + *kind = page21; + if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + *addend = 0; + return std::error_code(); + case ARM64_RELOC_PAGEOFF12 | rExtern | rLength4: + // ex: ldr x0, [x1, _foo@PAGEOFF] + *kind = offset12KindFromInstruction(*(const little32_t *)fixupContent); + if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + *addend = 0; + return std::error_code(); + case ARM64_RELOC_GOT_LOAD_PAGE21 | rPcRel | rExtern | rLength4: + // ex: adrp x1, _foo@GOTPAGE + *kind = gotPage21; + if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + *addend = 0; + return std::error_code(); + case ARM64_RELOC_GOT_LOAD_PAGEOFF12 | rExtern | rLength4: + // ex: ldr x0, [x1, _foo@GOTPAGEOFF] + *kind = gotOffset12; + if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + *addend = 0; + return std::error_code(); + case ARM64_RELOC_TLVP_LOAD_PAGE21 | rPcRel | rExtern | rLength4: + // ex: adrp x1, _foo@TLVPAGE + *kind = tlvPage21; + if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + *addend = 0; + return std::error_code(); + case ARM64_RELOC_TLVP_LOAD_PAGEOFF12 | rExtern | rLength4: + // ex: ldr x0, [x1, _foo@TLVPAGEOFF] + *kind = tlvOffset12; + if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + *addend = 0; + return std::error_code(); + case ARM64_RELOC_UNSIGNED | rExtern | rLength8: + // ex: .quad _foo + N + *kind = pointer64; + if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + *addend = *(const little64_t *)fixupContent; + return std::error_code(); + case ARM64_RELOC_UNSIGNED | rLength8: + // ex: .quad Lfoo + N + *kind = pointer64; + return atomFromAddress(reloc.symbol, *(const little64_t *)fixupContent, + target, addend); + case ARM64_RELOC_POINTER_TO_GOT | rExtern | rLength8: + // ex: .quad _foo@GOT + *kind = pointer64ToGOT; + if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + *addend = 0; + return std::error_code(); + case ARM64_RELOC_POINTER_TO_GOT | rPcRel | rExtern | rLength4: + // ex: .long _foo@GOT - . + *kind = delta32ToGOT; + if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + *addend = 0; + return std::error_code(); + default: + return make_dynamic_error_code(Twine("unsupported arm64 relocation type")); + } +} + +std::error_code ArchHandler_arm64::getPairReferenceInfo( + const normalized::Relocation &reloc1, const normalized::Relocation &reloc2, + const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, + bool swap, bool scatterable, FindAtomBySectionAndAddress atomFromAddress, + FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, + const lld::Atom **target, Reference::Addend *addend) { + const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; + const uint32_t *cont32 = reinterpret_cast<const uint32_t *>(fixupContent); + switch (relocPattern(reloc1) << 16 | relocPattern(reloc2)) { + case ((ARM64_RELOC_ADDEND | rLength4) << 16 | + ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4): + // ex: bl _foo+8 + *kind = branch26; + if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) + return ec; + *addend = reloc1.symbol; + return std::error_code(); + case ((ARM64_RELOC_ADDEND | rLength4) << 16 | + ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4): + // ex: adrp x1, _foo@PAGE + *kind = page21; + if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) + return ec; + *addend = reloc1.symbol; + return std::error_code(); + case ((ARM64_RELOC_ADDEND | rLength4) << 16 | + ARM64_RELOC_PAGEOFF12 | rExtern | rLength4): + // ex: ldr w0, [x1, _foo@PAGEOFF] + *kind = offset12KindFromInstruction(*cont32); + if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) + return ec; + *addend = reloc1.symbol; + return std::error_code(); + case ((ARM64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 | + ARM64_RELOC_UNSIGNED | rExtern | rLength8): + // ex: .quad _foo - . + *kind = delta64; + if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) + return ec; + *addend = (int64_t)*(const little64_t *)fixupContent + offsetInAtom; + return std::error_code(); + case ((ARM64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 | + ARM64_RELOC_UNSIGNED | rExtern | rLength4): + // ex: .quad _foo - . + *kind = delta32; + if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) + return ec; + *addend = (int32_t)*(const little32_t *)fixupContent + offsetInAtom; + return std::error_code(); + default: + return make_dynamic_error_code(Twine("unsupported arm64 relocation pair")); + } +} + +void ArchHandler_arm64::generateAtomContent( + const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress, + FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress, + uint8_t *atomContentBuffer) { + // Copy raw bytes. + memcpy(atomContentBuffer, atom.rawContent().data(), atom.size()); + // Apply fix-ups. + for (const Reference *ref : atom) { + uint32_t offset = ref->offsetInAtom(); + const Atom *target = ref->target(); + bool targetUnnamed = target->name().empty(); + uint64_t targetAddress = 0; + if (isa<DefinedAtom>(target)) + targetAddress = findAddress(*target); + uint64_t atomAddress = findAddress(atom); + uint64_t fixupAddress = atomAddress + offset; + if (relocatable) { + applyFixupRelocatable(*ref, &atomContentBuffer[offset], fixupAddress, + targetAddress, atomAddress, targetUnnamed); + } else { + applyFixupFinal(*ref, &atomContentBuffer[offset], fixupAddress, + targetAddress, atomAddress, imageBaseAddress, + findSectionAddress); + } + } +} + +void ArchHandler_arm64::applyFixupFinal(const Reference &ref, uint8_t *loc, + uint64_t fixupAddress, + uint64_t targetAddress, + uint64_t inAtomAddress, + uint64_t imageBaseAddress, + FindAddressForAtom findSectionAddress) { + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) + return; + assert(ref.kindArch() == Reference::KindArch::AArch64); + ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc); + ulittle64_t *loc64 = reinterpret_cast<ulittle64_t *>(loc); + int32_t displacement; + uint32_t instruction; + uint32_t value32; + uint32_t value64; + switch (static_cast<Arm64Kind>(ref.kindValue())) { + case branch26: + displacement = (targetAddress - fixupAddress) + ref.addend(); + *loc32 = setDisplacementInBranch26(*loc32, displacement); + return; + case page21: + case gotPage21: + case tlvPage21: + displacement = + ((targetAddress + ref.addend()) & (-4096)) - (fixupAddress & (-4096)); + *loc32 = setDisplacementInADRP(*loc32, displacement); + return; + case offset12: + case gotOffset12: + case tlvOffset12: + displacement = (targetAddress + ref.addend()) & 0x00000FFF; + *loc32 = setImm12(*loc32, displacement); + return; + case offset12scale2: + displacement = (targetAddress + ref.addend()) & 0x00000FFF; + assert(((displacement & 0x1) == 0) && + "scaled imm12 not accessing 2-byte aligneds"); + *loc32 = setImm12(*loc32, displacement >> 1); + return; + case offset12scale4: + displacement = (targetAddress + ref.addend()) & 0x00000FFF; + assert(((displacement & 0x3) == 0) && + "scaled imm12 not accessing 4-byte aligned"); + *loc32 = setImm12(*loc32, displacement >> 2); + return; + case offset12scale8: + displacement = (targetAddress + ref.addend()) & 0x00000FFF; + assert(((displacement & 0x7) == 0) && + "scaled imm12 not accessing 8-byte aligned"); + *loc32 = setImm12(*loc32, displacement >> 3); + return; + case offset12scale16: + displacement = (targetAddress + ref.addend()) & 0x00000FFF; + assert(((displacement & 0xF) == 0) && + "scaled imm12 not accessing 16-byte aligned"); + *loc32 = setImm12(*loc32, displacement >> 4); + return; + case addOffset12: + instruction = *loc32; + assert(((instruction & 0xFFC00000) == 0xF9400000) && + "GOT reloc is not an LDR instruction"); + displacement = (targetAddress + ref.addend()) & 0x00000FFF; + value32 = 0x91000000 | (instruction & 0x000003FF); + instruction = setImm12(value32, displacement); + *loc32 = instruction; + return; + case pointer64: + case pointer64ToGOT: + *loc64 = targetAddress + ref.addend(); + return; + case delta64: + case unwindFDEToFunction: + *loc64 = (targetAddress - fixupAddress) + ref.addend(); + return; + case delta32: + case delta32ToGOT: + *loc32 = (targetAddress - fixupAddress) + ref.addend(); + return; + case negDelta32: + *loc32 = fixupAddress - targetAddress + ref.addend(); + return; + case lazyPointer: + // Do nothing + return; + case lazyImmediateLocation: + *loc32 = ref.addend(); + return; + case imageOffset: + *loc32 = (targetAddress - imageBaseAddress) + ref.addend(); + return; + case imageOffsetGot: + llvm_unreachable("imageOffsetGot should have been changed to imageOffset"); + break; + case unwindInfoToEhFrame: + value64 = targetAddress - findSectionAddress(*ref.target()) + ref.addend(); + assert(value64 < 0xffffffU && "offset in __eh_frame too large"); + *loc32 = (*loc32 & 0xff000000U) | value64; + return; + case invalid: + // Fall into llvm_unreachable(). + break; + } + llvm_unreachable("invalid arm64 Reference Kind"); +} + +void ArchHandler_arm64::applyFixupRelocatable(const Reference &ref, + uint8_t *loc, + uint64_t fixupAddress, + uint64_t targetAddress, + uint64_t inAtomAddress, + bool targetUnnamed) { + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) + return; + assert(ref.kindArch() == Reference::KindArch::AArch64); + ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc); + ulittle64_t *loc64 = reinterpret_cast<ulittle64_t *>(loc); + switch (static_cast<Arm64Kind>(ref.kindValue())) { + case branch26: + *loc32 = setDisplacementInBranch26(*loc32, 0); + return; + case page21: + case gotPage21: + case tlvPage21: + *loc32 = setDisplacementInADRP(*loc32, 0); + return; + case offset12: + case offset12scale2: + case offset12scale4: + case offset12scale8: + case offset12scale16: + case gotOffset12: + case tlvOffset12: + *loc32 = setImm12(*loc32, 0); + return; + case pointer64: + if (targetUnnamed) + *loc64 = targetAddress + ref.addend(); + else + *loc64 = ref.addend(); + return; + case delta64: + *loc64 = ref.addend() + inAtomAddress - fixupAddress; + return; + case delta32: + *loc32 = ref.addend() + inAtomAddress - fixupAddress; + return; + case negDelta32: + *loc32 = fixupAddress - inAtomAddress + ref.addend(); + return; + case pointer64ToGOT: + *loc64 = 0; + return; + case delta32ToGOT: + *loc32 = -fixupAddress; + return; + case addOffset12: + llvm_unreachable("lazy reference kind implies GOT pass was run"); + case lazyPointer: + case lazyImmediateLocation: + llvm_unreachable("lazy reference kind implies Stubs pass was run"); + case imageOffset: + case imageOffsetGot: + case unwindInfoToEhFrame: + llvm_unreachable("fixup implies __unwind_info"); + return; + case unwindFDEToFunction: + // Do nothing for now + return; + case invalid: + // Fall into llvm_unreachable(). + break; + } + llvm_unreachable("unknown arm64 Reference Kind"); +} + +void ArchHandler_arm64::appendSectionRelocations( + const DefinedAtom &atom, uint64_t atomSectionOffset, const Reference &ref, + FindSymbolIndexForAtom symbolIndexForAtom, + FindSectionIndexForAtom sectionIndexForAtom, + FindAddressForAtom addressForAtom, normalized::Relocations &relocs) { + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) + return; + assert(ref.kindArch() == Reference::KindArch::AArch64); + uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom(); + switch (static_cast<Arm64Kind>(ref.kindValue())) { + case branch26: + if (ref.addend()) { + appendReloc(relocs, sectionOffset, ref.addend(), 0, + ARM64_RELOC_ADDEND | rLength4); + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4); + } else { + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4); + } + return; + case page21: + if (ref.addend()) { + appendReloc(relocs, sectionOffset, ref.addend(), 0, + ARM64_RELOC_ADDEND | rLength4); + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4); + } else { + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4); + } + return; + case offset12: + case offset12scale2: + case offset12scale4: + case offset12scale8: + case offset12scale16: + if (ref.addend()) { + appendReloc(relocs, sectionOffset, ref.addend(), 0, + ARM64_RELOC_ADDEND | rLength4); + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM64_RELOC_PAGEOFF12 | rExtern | rLength4); + } else { + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM64_RELOC_PAGEOFF12 | rExtern | rLength4); + } + return; + case gotPage21: + assert(ref.addend() == 0); + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM64_RELOC_GOT_LOAD_PAGE21 | rPcRel | rExtern | rLength4); + return; + case gotOffset12: + assert(ref.addend() == 0); + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM64_RELOC_GOT_LOAD_PAGEOFF12 | rExtern | rLength4); + return; + case tlvPage21: + assert(ref.addend() == 0); + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM64_RELOC_TLVP_LOAD_PAGE21 | rPcRel | rExtern | rLength4); + return; + case tlvOffset12: + assert(ref.addend() == 0); + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM64_RELOC_TLVP_LOAD_PAGEOFF12 | rExtern | rLength4); + return; + case pointer64: + if (ref.target()->name().empty()) + appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, + ARM64_RELOC_UNSIGNED | rLength8); + else + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM64_RELOC_UNSIGNED | rExtern | rLength8); + return; + case delta64: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, + ARM64_RELOC_SUBTRACTOR | rExtern | rLength8); + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM64_RELOC_UNSIGNED | rExtern | rLength8); + return; + case delta32: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, + ARM64_RELOC_SUBTRACTOR | rExtern | rLength4 ); + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM64_RELOC_UNSIGNED | rExtern | rLength4 ); + return; + case pointer64ToGOT: + assert(ref.addend() == 0); + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM64_RELOC_POINTER_TO_GOT | rExtern | rLength8); + return; + case delta32ToGOT: + assert(ref.addend() == 0); + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + ARM64_RELOC_POINTER_TO_GOT | rPcRel | rExtern | rLength4); + return; + case addOffset12: + llvm_unreachable("lazy reference kind implies GOT pass was run"); + case lazyPointer: + case lazyImmediateLocation: + llvm_unreachable("lazy reference kind implies Stubs pass was run"); + case imageOffset: + case imageOffsetGot: + llvm_unreachable("deltas from mach_header can only be in final images"); + case unwindFDEToFunction: + case unwindInfoToEhFrame: + case negDelta32: + // Do nothing. + return; + case invalid: + // Fall into llvm_unreachable(). + break; + } + llvm_unreachable("unknown arm64 Reference Kind"); +} + +std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_arm64() { + return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_arm64()); +} + +} // namespace mach_o +} // namespace lld diff --git a/lib/ReaderWriter/MachO/ArchHandler_x86.cpp b/lib/ReaderWriter/MachO/ArchHandler_x86.cpp new file mode 100644 index 0000000000000..19c8780e707ad --- /dev/null +++ b/lib/ReaderWriter/MachO/ArchHandler_x86.cpp @@ -0,0 +1,642 @@ +//===- lib/FileFormat/MachO/ArchHandler_x86.cpp ---------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ArchHandler.h" +#include "Atoms.h" +#include "MachONormalizedFileBinaryUtils.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace llvm::MachO; +using namespace lld::mach_o::normalized; + +namespace lld { +namespace mach_o { + +using llvm::support::ulittle16_t; +using llvm::support::ulittle32_t; + +using llvm::support::little16_t; +using llvm::support::little32_t; + +class ArchHandler_x86 : public ArchHandler { +public: + ArchHandler_x86(); + virtual ~ArchHandler_x86(); + + const Registry::KindStrings *kindStrings() override { return _sKindStrings; } + + Reference::KindArch kindArch() override { return Reference::KindArch::x86; } + + const StubInfo &stubInfo() override { return _sStubInfo; } + bool isCallSite(const Reference &) override; + bool isNonCallBranch(const Reference &) override { + return false; + } + + bool isPointer(const Reference &) override; + bool isPairedReloc(const normalized::Relocation &) override; + + bool needsCompactUnwind() override { + return false; + } + Reference::KindValue imageOffsetKind() override { + return invalid; + } + Reference::KindValue imageOffsetKindIndirect() override { + return invalid; + } + + Reference::KindValue unwindRefToCIEKind() override { + return negDelta32; + } + + Reference::KindValue unwindRefToFunctionKind() override{ + return delta32; + } + + Reference::KindValue unwindRefToEhFrameKind() override { + return invalid; + } + + + uint32_t dwarfCompactUnwindType() override { + return 0x04000000U; + } + + std::error_code getReferenceInfo(const normalized::Relocation &reloc, + const DefinedAtom *inAtom, + uint32_t offsetInAtom, + uint64_t fixupAddress, bool swap, + FindAtomBySectionAndAddress atomFromAddress, + FindAtomBySymbolIndex atomFromSymbolIndex, + Reference::KindValue *kind, + const lld::Atom **target, + Reference::Addend *addend) override; + std::error_code + getPairReferenceInfo(const normalized::Relocation &reloc1, + const normalized::Relocation &reloc2, + const DefinedAtom *inAtom, + uint32_t offsetInAtom, + uint64_t fixupAddress, bool swap, bool scatterable, + FindAtomBySectionAndAddress atomFromAddress, + FindAtomBySymbolIndex atomFromSymbolIndex, + Reference::KindValue *kind, + const lld::Atom **target, + Reference::Addend *addend) override; + + void generateAtomContent(const DefinedAtom &atom, bool relocatable, + FindAddressForAtom findAddress, + FindAddressForAtom findSectionAddress, + uint64_t imageBaseAddress, + uint8_t *atomContentBuffer) override; + + void appendSectionRelocations(const DefinedAtom &atom, + uint64_t atomSectionOffset, + const Reference &ref, + FindSymbolIndexForAtom symbolIndexForAtom, + FindSectionIndexForAtom sectionIndexForAtom, + FindAddressForAtom addressForAtom, + normalized::Relocations &relocs) override; + + bool isDataInCodeTransition(Reference::KindValue refKind) override { + switch (refKind) { + case modeCode: + case modeData: + return true; + default: + return false; + break; + } + } + + Reference::KindValue dataInCodeTransitionStart( + const MachODefinedAtom &atom) override { + return modeData; + } + + Reference::KindValue dataInCodeTransitionEnd( + const MachODefinedAtom &atom) override { + return modeCode; + } + +private: + static const Registry::KindStrings _sKindStrings[]; + static const StubInfo _sStubInfo; + + enum X86Kind : Reference::KindValue { + invalid, /// for error condition + + modeCode, /// Content starting at this offset is code. + modeData, /// Content starting at this offset is data. + + // Kinds found in mach-o .o files: + branch32, /// ex: call _foo + branch16, /// ex: callw _foo + abs32, /// ex: movl _foo, %eax + funcRel32, /// ex: movl _foo-L1(%eax), %eax + pointer32, /// ex: .long _foo + delta32, /// ex: .long _foo - . + negDelta32, /// ex: .long . - _foo + + // Kinds introduced by Passes: + lazyPointer, /// Location contains a lazy pointer. + lazyImmediateLocation, /// Location contains immediate value used in stub. + }; + + static bool useExternalRelocationTo(const Atom &target); + + void applyFixupFinal(const Reference &ref, uint8_t *location, + uint64_t fixupAddress, uint64_t targetAddress, + uint64_t inAtomAddress); + + void applyFixupRelocatable(const Reference &ref, uint8_t *location, + uint64_t fixupAddress, + uint64_t targetAddress, + uint64_t inAtomAddress); +}; + +//===----------------------------------------------------------------------===// +// ArchHandler_x86 +//===----------------------------------------------------------------------===// + +ArchHandler_x86::ArchHandler_x86() {} + +ArchHandler_x86::~ArchHandler_x86() { } + +const Registry::KindStrings ArchHandler_x86::_sKindStrings[] = { + LLD_KIND_STRING_ENTRY(invalid), + LLD_KIND_STRING_ENTRY(modeCode), + LLD_KIND_STRING_ENTRY(modeData), + LLD_KIND_STRING_ENTRY(branch32), + LLD_KIND_STRING_ENTRY(branch16), + LLD_KIND_STRING_ENTRY(abs32), + LLD_KIND_STRING_ENTRY(funcRel32), + LLD_KIND_STRING_ENTRY(pointer32), + LLD_KIND_STRING_ENTRY(delta32), + LLD_KIND_STRING_ENTRY(negDelta32), + LLD_KIND_STRING_ENTRY(lazyPointer), + LLD_KIND_STRING_ENTRY(lazyImmediateLocation), + LLD_KIND_STRING_END +}; + +const ArchHandler::StubInfo ArchHandler_x86::_sStubInfo = { + "dyld_stub_binder", + + // Lazy pointer references + { Reference::KindArch::x86, pointer32, 0, 0 }, + { Reference::KindArch::x86, lazyPointer, 0, 0 }, + + // GOT pointer to dyld_stub_binder + { Reference::KindArch::x86, pointer32, 0, 0 }, + + // x86 code alignment + 1, + + // Stub size and code + 6, + { 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 }, // jmp *lazyPointer + { Reference::KindArch::x86, abs32, 2, 0 }, + { false, 0, 0, 0 }, + + // Stub Helper size and code + 10, + { 0x68, 0x00, 0x00, 0x00, 0x00, // pushl $lazy-info-offset + 0xE9, 0x00, 0x00, 0x00, 0x00 }, // jmp helperhelper + { Reference::KindArch::x86, lazyImmediateLocation, 1, 0 }, + { Reference::KindArch::x86, branch32, 6, 0 }, + + // Stub Helper-Common size and code + 12, + { 0x68, 0x00, 0x00, 0x00, 0x00, // pushl $dyld_ImageLoaderCache + 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *_fast_lazy_bind + 0x90 }, // nop + { Reference::KindArch::x86, abs32, 1, 0 }, + { false, 0, 0, 0 }, + { Reference::KindArch::x86, abs32, 7, 0 }, + { false, 0, 0, 0 } +}; + +bool ArchHandler_x86::isCallSite(const Reference &ref) { + return (ref.kindValue() == branch32); +} + +bool ArchHandler_x86::isPointer(const Reference &ref) { + return (ref.kindValue() == pointer32); +} + +bool ArchHandler_x86::isPairedReloc(const Relocation &reloc) { + if (!reloc.scattered) + return false; + return (reloc.type == GENERIC_RELOC_LOCAL_SECTDIFF) || + (reloc.type == GENERIC_RELOC_SECTDIFF); +} + +std::error_code +ArchHandler_x86::getReferenceInfo(const Relocation &reloc, + const DefinedAtom *inAtom, + uint32_t offsetInAtom, + uint64_t fixupAddress, bool swap, + FindAtomBySectionAndAddress atomFromAddress, + FindAtomBySymbolIndex atomFromSymbolIndex, + Reference::KindValue *kind, + const lld::Atom **target, + Reference::Addend *addend) { + typedef std::error_code E; + DefinedAtom::ContentPermissions perms; + const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; + uint64_t targetAddress; + switch (relocPattern(reloc)) { + case GENERIC_RELOC_VANILLA | rPcRel | rExtern | rLength4: + // ex: call _foo (and _foo undefined) + *kind = branch32; + if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + *addend = fixupAddress + 4 + (int32_t)*(const little32_t *)fixupContent; + break; + case GENERIC_RELOC_VANILLA | rPcRel | rLength4: + // ex: call _foo (and _foo defined) + *kind = branch32; + targetAddress = + fixupAddress + 4 + (int32_t) * (const little32_t *)fixupContent; + return atomFromAddress(reloc.symbol, targetAddress, target, addend); + break; + case GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength4: + // ex: call _foo+n (and _foo defined) + *kind = branch32; + targetAddress = + fixupAddress + 4 + (int32_t) * (const little32_t *)fixupContent; + if (E ec = atomFromAddress(0, reloc.value, target, addend)) + return ec; + *addend = targetAddress - reloc.value; + break; + case GENERIC_RELOC_VANILLA | rPcRel | rExtern | rLength2: + // ex: callw _foo (and _foo undefined) + *kind = branch16; + if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + *addend = fixupAddress + 2 + (int16_t)*(const little16_t *)fixupContent; + break; + case GENERIC_RELOC_VANILLA | rPcRel | rLength2: + // ex: callw _foo (and _foo defined) + *kind = branch16; + targetAddress = + fixupAddress + 2 + (int16_t) * (const little16_t *)fixupContent; + return atomFromAddress(reloc.symbol, targetAddress, target, addend); + break; + case GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength2: + // ex: callw _foo+n (and _foo defined) + *kind = branch16; + targetAddress = + fixupAddress + 2 + (int16_t) * (const little16_t *)fixupContent; + if (E ec = atomFromAddress(0, reloc.value, target, addend)) + return ec; + *addend = targetAddress - reloc.value; + break; + case GENERIC_RELOC_VANILLA | rExtern | rLength4: + // ex: movl _foo, %eax (and _foo undefined) + // ex: .long _foo (and _foo undefined) + perms = inAtom->permissions(); + *kind = + ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32 + : pointer32; + if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + *addend = *(const ulittle32_t *)fixupContent; + break; + case GENERIC_RELOC_VANILLA | rLength4: + // ex: movl _foo, %eax (and _foo defined) + // ex: .long _foo (and _foo defined) + perms = inAtom->permissions(); + *kind = + ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32 + : pointer32; + targetAddress = *(const ulittle32_t *)fixupContent; + return atomFromAddress(reloc.symbol, targetAddress, target, addend); + break; + case GENERIC_RELOC_VANILLA | rScattered | rLength4: + // ex: .long _foo+n (and _foo defined) + perms = inAtom->permissions(); + *kind = + ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) ? abs32 + : pointer32; + if (E ec = atomFromAddress(0, reloc.value, target, addend)) + return ec; + *addend = *(const ulittle32_t *)fixupContent - reloc.value; + break; + default: + return make_dynamic_error_code(Twine("unsupported i386 relocation type")); + } + return std::error_code(); +} + +std::error_code +ArchHandler_x86::getPairReferenceInfo(const normalized::Relocation &reloc1, + const normalized::Relocation &reloc2, + const DefinedAtom *inAtom, + uint32_t offsetInAtom, + uint64_t fixupAddress, bool swap, + bool scatterable, + FindAtomBySectionAndAddress atomFromAddr, + FindAtomBySymbolIndex atomFromSymbolIndex, + Reference::KindValue *kind, + const lld::Atom **target, + Reference::Addend *addend) { + const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; + std::error_code ec; + DefinedAtom::ContentPermissions perms = inAtom->permissions(); + uint32_t fromAddress; + uint32_t toAddress; + uint32_t value; + const lld::Atom *fromTarget; + Reference::Addend offsetInTo; + Reference::Addend offsetInFrom; + switch (relocPattern(reloc1) << 16 | relocPattern(reloc2)) { + case ((GENERIC_RELOC_SECTDIFF | rScattered | rLength4) << 16 | + GENERIC_RELOC_PAIR | rScattered | rLength4): + case ((GENERIC_RELOC_LOCAL_SECTDIFF | rScattered | rLength4) << 16 | + GENERIC_RELOC_PAIR | rScattered | rLength4): + toAddress = reloc1.value; + fromAddress = reloc2.value; + value = *(const little32_t *)fixupContent; + ec = atomFromAddr(0, toAddress, target, &offsetInTo); + if (ec) + return ec; + ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom); + if (ec) + return ec; + if (fromTarget != inAtom) { + if (*target != inAtom) + return make_dynamic_error_code(Twine("SECTDIFF relocation where " + "neither target is in atom")); + *kind = negDelta32; + *addend = toAddress - value - fromAddress; + *target = fromTarget; + } else { + if ((perms & DefinedAtom::permR_X) == DefinedAtom::permR_X) { + // SECTDIFF relocations are used in i386 codegen where the function + // prolog does a CALL to the next instruction which POPs the return + // address into EBX which becomes the pic-base register. The POP + // instruction is label the used for the subtrahend in expressions. + // The funcRel32 kind represents the 32-bit delta to some symbol from + // the start of the function (atom) containing the funcRel32. + *kind = funcRel32; + uint32_t ta = fromAddress + value - toAddress; + *addend = ta - offsetInFrom; + } else { + *kind = delta32; + *addend = fromAddress + value - toAddress; + } + } + return std::error_code(); + break; + default: + return make_dynamic_error_code(Twine("unsupported i386 relocation type")); + } +} + +void ArchHandler_x86::generateAtomContent(const DefinedAtom &atom, + bool relocatable, + FindAddressForAtom findAddress, + FindAddressForAtom findSectionAddress, + uint64_t imageBaseAddress, + uint8_t *atomContentBuffer) { + // Copy raw bytes. + memcpy(atomContentBuffer, atom.rawContent().data(), atom.size()); + // Apply fix-ups. + for (const Reference *ref : atom) { + uint32_t offset = ref->offsetInAtom(); + const Atom *target = ref->target(); + uint64_t targetAddress = 0; + if (isa<DefinedAtom>(target)) + targetAddress = findAddress(*target); + uint64_t atomAddress = findAddress(atom); + uint64_t fixupAddress = atomAddress + offset; + if (relocatable) { + applyFixupRelocatable(*ref, &atomContentBuffer[offset], + fixupAddress, targetAddress, + atomAddress); + } else { + applyFixupFinal(*ref, &atomContentBuffer[offset], + fixupAddress, targetAddress, + atomAddress); + } + } +} + +void ArchHandler_x86::applyFixupFinal(const Reference &ref, uint8_t *loc, + uint64_t fixupAddress, + uint64_t targetAddress, + uint64_t inAtomAddress) { + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) + return; + assert(ref.kindArch() == Reference::KindArch::x86); + ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc); + switch (static_cast<X86Kind>(ref.kindValue())) { + case branch32: + *loc32 = (targetAddress - (fixupAddress + 4)) + ref.addend(); + break; + case branch16: + *loc32 = (targetAddress - (fixupAddress + 2)) + ref.addend(); + break; + case pointer32: + case abs32: + *loc32 = targetAddress + ref.addend(); + break; + case funcRel32: + *loc32 = targetAddress - inAtomAddress + ref.addend(); + break; + case delta32: + *loc32 = targetAddress - fixupAddress + ref.addend(); + break; + case negDelta32: + *loc32 = fixupAddress - targetAddress + ref.addend(); + break; + case modeCode: + case modeData: + case lazyPointer: + // do nothing + break; + case lazyImmediateLocation: + *loc32 = ref.addend(); + break; + case invalid: + llvm_unreachable("invalid x86 Reference Kind"); + break; + } +} + +void ArchHandler_x86::applyFixupRelocatable(const Reference &ref, + uint8_t *loc, + uint64_t fixupAddress, + uint64_t targetAddress, + uint64_t inAtomAddress) { + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) + return; + assert(ref.kindArch() == Reference::KindArch::x86); + bool useExternalReloc = useExternalRelocationTo(*ref.target()); + ulittle16_t *loc16 = reinterpret_cast<ulittle16_t *>(loc); + ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc); + switch (static_cast<X86Kind>(ref.kindValue())) { + case branch32: + if (useExternalReloc) + *loc32 = ref.addend() - (fixupAddress + 4); + else + *loc32 =(targetAddress - (fixupAddress+4)) + ref.addend(); + break; + case branch16: + if (useExternalReloc) + *loc16 = ref.addend() - (fixupAddress + 2); + else + *loc16 = (targetAddress - (fixupAddress+2)) + ref.addend(); + break; + case pointer32: + case abs32: + *loc32 = targetAddress + ref.addend(); + break; + case funcRel32: + *loc32 = targetAddress - inAtomAddress + ref.addend(); // FIXME + break; + case delta32: + *loc32 = targetAddress - fixupAddress + ref.addend(); + break; + case negDelta32: + *loc32 = fixupAddress - targetAddress + ref.addend(); + break; + case modeCode: + case modeData: + case lazyPointer: + case lazyImmediateLocation: + // do nothing + break; + case invalid: + llvm_unreachable("invalid x86 Reference Kind"); + break; + } +} + +bool ArchHandler_x86::useExternalRelocationTo(const Atom &target) { + // Undefined symbols are referenced via external relocations. + if (isa<UndefinedAtom>(&target)) + return true; + if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(&target)) { + switch (defAtom->merge()) { + case DefinedAtom::mergeAsTentative: + // Tentative definitions are referenced via external relocations. + return true; + case DefinedAtom::mergeAsWeak: + case DefinedAtom::mergeAsWeakAndAddressUsed: + // Global weak-defs are referenced via external relocations. + return (defAtom->scope() == DefinedAtom::scopeGlobal); + default: + break; + } + } + // Everything else is reference via an internal relocation. + return false; +} + + +void ArchHandler_x86::appendSectionRelocations( + const DefinedAtom &atom, + uint64_t atomSectionOffset, + const Reference &ref, + FindSymbolIndexForAtom symbolIndexForAtom, + FindSectionIndexForAtom sectionIndexForAtom, + FindAddressForAtom addressForAtom, + normalized::Relocations &relocs) { + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) + return; + assert(ref.kindArch() == Reference::KindArch::x86); + uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom(); + bool useExternalReloc = useExternalRelocationTo(*ref.target()); + switch (static_cast<X86Kind>(ref.kindValue())) { + case modeCode: + case modeData: + break; + case branch32: + if (useExternalReloc) { + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + GENERIC_RELOC_VANILLA | rExtern | rPcRel | rLength4); + } else { + if (ref.addend() != 0) + appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), + GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength4); + else + appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, + GENERIC_RELOC_VANILLA | rPcRel | rLength4); + } + break; + case branch16: + if (useExternalReloc) { + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + GENERIC_RELOC_VANILLA | rExtern | rPcRel | rLength2); + } else { + if (ref.addend() != 0) + appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), + GENERIC_RELOC_VANILLA | rScattered | rPcRel | rLength2); + else + appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, + GENERIC_RELOC_VANILLA | rPcRel | rLength2); + } + break; + case pointer32: + case abs32: + if (useExternalReloc) + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + GENERIC_RELOC_VANILLA | rExtern | rLength4); + else { + if (ref.addend() != 0) + appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), + GENERIC_RELOC_VANILLA | rScattered | rLength4); + else + appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, + GENERIC_RELOC_VANILLA | rLength4); + } + break; + case funcRel32: + appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), + GENERIC_RELOC_SECTDIFF | rScattered | rLength4); + appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) - ref.addend(), + GENERIC_RELOC_PAIR | rScattered | rLength4); + break; + case delta32: + appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), + GENERIC_RELOC_SECTDIFF | rScattered | rLength4); + appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) + + ref.offsetInAtom(), + GENERIC_RELOC_PAIR | rScattered | rLength4); + break; + case negDelta32: + appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) + + ref.offsetInAtom(), + GENERIC_RELOC_SECTDIFF | rScattered | rLength4); + appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), + GENERIC_RELOC_PAIR | rScattered | rLength4); + break; + case lazyPointer: + case lazyImmediateLocation: + llvm_unreachable("lazy reference kind implies Stubs pass was run"); + break; + case invalid: + llvm_unreachable("unknown x86 Reference Kind"); + break; + } +} + + +std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_x86() { + return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_x86()); +} + +} // namespace mach_o +} // namespace lld diff --git a/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp b/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp new file mode 100644 index 0000000000000..81fe1af42d7e6 --- /dev/null +++ b/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp @@ -0,0 +1,723 @@ +//===- lib/FileFormat/MachO/ArchHandler_x86_64.cpp ------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ArchHandler.h" +#include "Atoms.h" +#include "MachONormalizedFileBinaryUtils.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace llvm::MachO; +using namespace lld::mach_o::normalized; + +namespace lld { +namespace mach_o { + +using llvm::support::ulittle32_t; +using llvm::support::ulittle64_t; + +using llvm::support::little32_t; +using llvm::support::little64_t; + +class ArchHandler_x86_64 : public ArchHandler { +public: + ArchHandler_x86_64(); + virtual ~ArchHandler_x86_64(); + + const Registry::KindStrings *kindStrings() override { return _sKindStrings; } + + Reference::KindArch kindArch() override { + return Reference::KindArch::x86_64; + } + + /// Used by GOTPass to locate GOT References + bool isGOTAccess(const Reference &ref, bool &canBypassGOT) override { + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) + return false; + assert(ref.kindArch() == Reference::KindArch::x86_64); + switch (ref.kindValue()) { + case ripRel32GotLoad: + canBypassGOT = true; + return true; + case ripRel32Got: + canBypassGOT = false; + return true; + case imageOffsetGot: + canBypassGOT = false; + return true; + default: + return false; + } + } + + /// Used by GOTPass to update GOT References + void updateReferenceToGOT(const Reference *ref, bool targetNowGOT) override { + assert(ref->kindNamespace() == Reference::KindNamespace::mach_o); + assert(ref->kindArch() == Reference::KindArch::x86_64); + + switch (ref->kindValue()) { + case ripRel32Got: + assert(targetNowGOT && "target must be GOT"); + case ripRel32GotLoad: + const_cast<Reference *>(ref) + ->setKindValue(targetNowGOT ? ripRel32 : ripRel32GotLoadNowLea); + break; + case imageOffsetGot: + const_cast<Reference *>(ref)->setKindValue(imageOffset); + break; + default: + llvm_unreachable("unknown GOT reference kind"); + } + } + + bool needsCompactUnwind() override { + return true; + } + Reference::KindValue imageOffsetKind() override { + return imageOffset; + } + Reference::KindValue imageOffsetKindIndirect() override { + return imageOffsetGot; + } + + Reference::KindValue unwindRefToCIEKind() override { + return negDelta32; + } + + Reference::KindValue unwindRefToFunctionKind() override{ + return unwindFDEToFunction; + } + + Reference::KindValue unwindRefToEhFrameKind() override { + return unwindInfoToEhFrame; + } + + uint32_t dwarfCompactUnwindType() override { + return 0x04000000U; + } + + const StubInfo &stubInfo() override { return _sStubInfo; } + + bool isNonCallBranch(const Reference &) override { + return false; + } + + bool isCallSite(const Reference &) override; + bool isPointer(const Reference &) override; + bool isPairedReloc(const normalized::Relocation &) override; + + std::error_code getReferenceInfo(const normalized::Relocation &reloc, + const DefinedAtom *inAtom, + uint32_t offsetInAtom, + uint64_t fixupAddress, bool swap, + FindAtomBySectionAndAddress atomFromAddress, + FindAtomBySymbolIndex atomFromSymbolIndex, + Reference::KindValue *kind, + const lld::Atom **target, + Reference::Addend *addend) override; + std::error_code + getPairReferenceInfo(const normalized::Relocation &reloc1, + const normalized::Relocation &reloc2, + const DefinedAtom *inAtom, + uint32_t offsetInAtom, + uint64_t fixupAddress, bool swap, bool scatterable, + FindAtomBySectionAndAddress atomFromAddress, + FindAtomBySymbolIndex atomFromSymbolIndex, + Reference::KindValue *kind, + const lld::Atom **target, + Reference::Addend *addend) override; + + bool needsLocalSymbolInRelocatableFile(const DefinedAtom *atom) override { + return (atom->contentType() == DefinedAtom::typeCString); + } + + void generateAtomContent(const DefinedAtom &atom, bool relocatable, + FindAddressForAtom findAddress, + FindAddressForAtom findSectionAddress, + uint64_t imageBase, + uint8_t *atomContentBuffer) override; + + void appendSectionRelocations(const DefinedAtom &atom, + uint64_t atomSectionOffset, + const Reference &ref, + FindSymbolIndexForAtom symbolIndexForAtom, + FindSectionIndexForAtom sectionIndexForAtom, + FindAddressForAtom addressForAtom, + normalized::Relocations &relocs) override; + +private: + static const Registry::KindStrings _sKindStrings[]; + static const StubInfo _sStubInfo; + + enum X86_64Kind: Reference::KindValue { + invalid, /// for error condition + + // Kinds found in mach-o .o files: + branch32, /// ex: call _foo + ripRel32, /// ex: movq _foo(%rip), %rax + ripRel32Minus1, /// ex: movb $0x12, _foo(%rip) + ripRel32Minus2, /// ex: movw $0x1234, _foo(%rip) + ripRel32Minus4, /// ex: movl $0x12345678, _foo(%rip) + ripRel32Anon, /// ex: movq L1(%rip), %rax + ripRel32GotLoad, /// ex: movq _foo@GOTPCREL(%rip), %rax + ripRel32Got, /// ex: pushq _foo@GOTPCREL(%rip) + pointer64, /// ex: .quad _foo + pointer64Anon, /// ex: .quad L1 + delta64, /// ex: .quad _foo - . + delta32, /// ex: .long _foo - . + delta64Anon, /// ex: .quad L1 - . + delta32Anon, /// ex: .long L1 - . + negDelta32, /// ex: .long . - _foo + + // Kinds introduced by Passes: + ripRel32GotLoadNowLea, /// Target of GOT load is in linkage unit so + /// "movq _foo@GOTPCREL(%rip), %rax" can be changed + /// to "leaq _foo(%rip), %rax + lazyPointer, /// Location contains a lazy pointer. + lazyImmediateLocation, /// Location contains immediate value used in stub. + + imageOffset, /// Location contains offset of atom in final image + imageOffsetGot, /// Location contains offset of GOT entry for atom in + /// final image (typically personality function). + unwindFDEToFunction, /// Nearly delta64, but cannot be rematerialized in + /// relocatable object (yay for implicit contracts!). + unwindInfoToEhFrame, /// Fix low 24 bits of compact unwind encoding to + /// refer to __eh_frame entry. + }; + + Reference::KindValue kindFromReloc(const normalized::Relocation &reloc); + Reference::KindValue kindFromRelocPair(const normalized::Relocation &reloc1, + const normalized::Relocation &reloc2); + + void applyFixupFinal(const Reference &ref, uint8_t *location, + uint64_t fixupAddress, uint64_t targetAddress, + uint64_t inAtomAddress, uint64_t imageBaseAddress, + FindAddressForAtom findSectionAddress); + + void applyFixupRelocatable(const Reference &ref, uint8_t *location, + uint64_t fixupAddress, + uint64_t targetAddress, + uint64_t inAtomAddress); +}; + + +ArchHandler_x86_64::ArchHandler_x86_64() { } + +ArchHandler_x86_64::~ArchHandler_x86_64() { } + +const Registry::KindStrings ArchHandler_x86_64::_sKindStrings[] = { + LLD_KIND_STRING_ENTRY(invalid), LLD_KIND_STRING_ENTRY(branch32), + LLD_KIND_STRING_ENTRY(ripRel32), LLD_KIND_STRING_ENTRY(ripRel32Minus1), + LLD_KIND_STRING_ENTRY(ripRel32Minus2), LLD_KIND_STRING_ENTRY(ripRel32Minus4), + LLD_KIND_STRING_ENTRY(ripRel32Anon), LLD_KIND_STRING_ENTRY(ripRel32GotLoad), + LLD_KIND_STRING_ENTRY(ripRel32GotLoadNowLea), + LLD_KIND_STRING_ENTRY(ripRel32Got), LLD_KIND_STRING_ENTRY(lazyPointer), + LLD_KIND_STRING_ENTRY(lazyImmediateLocation), + LLD_KIND_STRING_ENTRY(pointer64), LLD_KIND_STRING_ENTRY(pointer64Anon), + LLD_KIND_STRING_ENTRY(delta32), LLD_KIND_STRING_ENTRY(delta64), + LLD_KIND_STRING_ENTRY(delta32Anon), LLD_KIND_STRING_ENTRY(delta64Anon), + LLD_KIND_STRING_ENTRY(negDelta32), + LLD_KIND_STRING_ENTRY(imageOffset), LLD_KIND_STRING_ENTRY(imageOffsetGot), + LLD_KIND_STRING_ENTRY(unwindFDEToFunction), + LLD_KIND_STRING_ENTRY(unwindInfoToEhFrame), + LLD_KIND_STRING_END +}; + +const ArchHandler::StubInfo ArchHandler_x86_64::_sStubInfo = { + "dyld_stub_binder", + + // Lazy pointer references + { Reference::KindArch::x86_64, pointer64, 0, 0 }, + { Reference::KindArch::x86_64, lazyPointer, 0, 0 }, + + // GOT pointer to dyld_stub_binder + { Reference::KindArch::x86_64, pointer64, 0, 0 }, + + // x86_64 code alignment 2^1 + 1, + + // Stub size and code + 6, + { 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 }, // jmp *lazyPointer + { Reference::KindArch::x86_64, ripRel32, 2, 0 }, + { false, 0, 0, 0 }, + + // Stub Helper size and code + 10, + { 0x68, 0x00, 0x00, 0x00, 0x00, // pushq $lazy-info-offset + 0xE9, 0x00, 0x00, 0x00, 0x00 }, // jmp helperhelper + { Reference::KindArch::x86_64, lazyImmediateLocation, 1, 0 }, + { Reference::KindArch::x86_64, branch32, 6, 0 }, + + // Stub Helper-Common size and code + 16, + { 0x4C, 0x8D, 0x1D, 0x00, 0x00, 0x00, 0x00, // leaq cache(%rip),%r11 + 0x41, 0x53, // push %r11 + 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *binder(%rip) + 0x90 }, // nop + { Reference::KindArch::x86_64, ripRel32, 3, 0 }, + { false, 0, 0, 0 }, + { Reference::KindArch::x86_64, ripRel32, 11, 0 }, + { false, 0, 0, 0 } + +}; + +bool ArchHandler_x86_64::isCallSite(const Reference &ref) { + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) + return false; + assert(ref.kindArch() == Reference::KindArch::x86_64); + return (ref.kindValue() == branch32); +} + +bool ArchHandler_x86_64::isPointer(const Reference &ref) { + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) + return false; + assert(ref.kindArch() == Reference::KindArch::x86_64); + Reference::KindValue kind = ref.kindValue(); + return (kind == pointer64 || kind == pointer64Anon); +} + +bool ArchHandler_x86_64::isPairedReloc(const Relocation &reloc) { + return (reloc.type == X86_64_RELOC_SUBTRACTOR); +} + +Reference::KindValue +ArchHandler_x86_64::kindFromReloc(const Relocation &reloc) { + switch(relocPattern(reloc)) { + case X86_64_RELOC_BRANCH | rPcRel | rExtern | rLength4: + return branch32; + case X86_64_RELOC_SIGNED | rPcRel | rExtern | rLength4: + return ripRel32; + case X86_64_RELOC_SIGNED | rPcRel | rLength4: + return ripRel32Anon; + case X86_64_RELOC_SIGNED_1 | rPcRel | rExtern | rLength4: + return ripRel32Minus1; + case X86_64_RELOC_SIGNED_2 | rPcRel | rExtern | rLength4: + return ripRel32Minus2; + case X86_64_RELOC_SIGNED_4 | rPcRel | rExtern | rLength4: + return ripRel32Minus4; + case X86_64_RELOC_GOT_LOAD | rPcRel | rExtern | rLength4: + return ripRel32GotLoad; + case X86_64_RELOC_GOT | rPcRel | rExtern | rLength4: + return ripRel32Got; + case X86_64_RELOC_UNSIGNED | rExtern | rLength8: + return pointer64; + case X86_64_RELOC_UNSIGNED | rLength8: + return pointer64Anon; + default: + return invalid; + } +} + +std::error_code +ArchHandler_x86_64::getReferenceInfo(const Relocation &reloc, + const DefinedAtom *inAtom, + uint32_t offsetInAtom, + uint64_t fixupAddress, bool swap, + FindAtomBySectionAndAddress atomFromAddress, + FindAtomBySymbolIndex atomFromSymbolIndex, + Reference::KindValue *kind, + const lld::Atom **target, + Reference::Addend *addend) { + typedef std::error_code E; + *kind = kindFromReloc(reloc); + if (*kind == invalid) + return make_dynamic_error_code(Twine("unknown type")); + const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; + uint64_t targetAddress; + switch (*kind) { + case branch32: + case ripRel32: + if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + *addend = *(const little32_t *)fixupContent; + return std::error_code(); + case ripRel32Minus1: + if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + *addend = (int32_t)*(const little32_t *)fixupContent + 1; + return std::error_code(); + case ripRel32Minus2: + if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + *addend = (int32_t)*(const little32_t *)fixupContent + 2; + return std::error_code(); + case ripRel32Minus4: + if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + *addend = (int32_t)*(const little32_t *)fixupContent + 4; + return std::error_code(); + case ripRel32Anon: + targetAddress = fixupAddress + 4 + *(const little32_t *)fixupContent; + return atomFromAddress(reloc.symbol, targetAddress, target, addend); + case ripRel32GotLoad: + case ripRel32Got: + if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + *addend = *(const little32_t *)fixupContent; + return std::error_code(); + case pointer64: + if (E ec = atomFromSymbolIndex(reloc.symbol, target)) + return ec; + *addend = *(const little64_t *)fixupContent; + return std::error_code(); + case pointer64Anon: + targetAddress = *(const little64_t *)fixupContent; + return atomFromAddress(reloc.symbol, targetAddress, target, addend); + default: + llvm_unreachable("bad reloc kind"); + } +} + +Reference::KindValue +ArchHandler_x86_64::kindFromRelocPair(const normalized::Relocation &reloc1, + const normalized::Relocation &reloc2) { + switch(relocPattern(reloc1) << 16 | relocPattern(reloc2)) { + case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 | + X86_64_RELOC_UNSIGNED | rExtern | rLength8): + return delta64; + case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 | + X86_64_RELOC_UNSIGNED | rExtern | rLength4): + return delta32; + case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 | + X86_64_RELOC_UNSIGNED | rLength8): + return delta64Anon; + case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 | + X86_64_RELOC_UNSIGNED | rLength4): + return delta32Anon; + default: + llvm_unreachable("bad reloc pairs"); + } +} + +std::error_code +ArchHandler_x86_64::getPairReferenceInfo(const normalized::Relocation &reloc1, + const normalized::Relocation &reloc2, + const DefinedAtom *inAtom, + uint32_t offsetInAtom, + uint64_t fixupAddress, bool swap, + bool scatterable, + FindAtomBySectionAndAddress atomFromAddress, + FindAtomBySymbolIndex atomFromSymbolIndex, + Reference::KindValue *kind, + const lld::Atom **target, + Reference::Addend *addend) { + *kind = kindFromRelocPair(reloc1, reloc2); + if (*kind == invalid) + return make_dynamic_error_code(Twine("unknown pair")); + const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; + typedef std::error_code E; + uint64_t targetAddress; + const lld::Atom *fromTarget; + if (E ec = atomFromSymbolIndex(reloc1.symbol, &fromTarget)) + return ec; + if (fromTarget != inAtom) + return make_dynamic_error_code(Twine("pointer diff not in base atom")); + switch (*kind) { + case delta64: + if (E ec = atomFromSymbolIndex(reloc2.symbol, target)) + return ec; + *addend = (int64_t)*(const little64_t *)fixupContent + offsetInAtom; + return std::error_code(); + case delta32: + if (E ec = atomFromSymbolIndex(reloc2.symbol, target)) + return ec; + *addend = (int32_t)*(const little32_t *)fixupContent + offsetInAtom; + return std::error_code(); + case delta64Anon: + targetAddress = offsetInAtom + (int64_t)*(const little64_t *)fixupContent; + return atomFromAddress(reloc2.symbol, targetAddress, target, addend); + case delta32Anon: + targetAddress = offsetInAtom + (int32_t)*(const little32_t *)fixupContent; + return atomFromAddress(reloc2.symbol, targetAddress, target, addend); + default: + llvm_unreachable("bad reloc pair kind"); + } +} + +void ArchHandler_x86_64::generateAtomContent( + const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress, + FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress, + uint8_t *atomContentBuffer) { + // Copy raw bytes. + memcpy(atomContentBuffer, atom.rawContent().data(), atom.size()); + // Apply fix-ups. + for (const Reference *ref : atom) { + uint32_t offset = ref->offsetInAtom(); + const Atom *target = ref->target(); + uint64_t targetAddress = 0; + if (isa<DefinedAtom>(target)) + targetAddress = findAddress(*target); + uint64_t atomAddress = findAddress(atom); + uint64_t fixupAddress = atomAddress + offset; + if (relocatable) { + applyFixupRelocatable(*ref, &atomContentBuffer[offset], + fixupAddress, targetAddress, + atomAddress); + } else { + applyFixupFinal(*ref, &atomContentBuffer[offset], + fixupAddress, targetAddress, + atomAddress, imageBaseAddress, findSectionAddress); + } + } +} + +void ArchHandler_x86_64::applyFixupFinal( + const Reference &ref, uint8_t *loc, uint64_t fixupAddress, + uint64_t targetAddress, uint64_t inAtomAddress, uint64_t imageBaseAddress, + FindAddressForAtom findSectionAddress) { + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) + return; + assert(ref.kindArch() == Reference::KindArch::x86_64); + ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc); + ulittle64_t *loc64 = reinterpret_cast<ulittle64_t *>(loc); + switch (static_cast<X86_64Kind>(ref.kindValue())) { + case branch32: + case ripRel32: + case ripRel32Anon: + case ripRel32Got: + case ripRel32GotLoad: + *loc32 = targetAddress - (fixupAddress + 4) + ref.addend(); + return; + case pointer64: + case pointer64Anon: + *loc64 = targetAddress + ref.addend(); + return; + case ripRel32Minus1: + *loc32 = targetAddress - (fixupAddress + 5) + ref.addend(); + return; + case ripRel32Minus2: + *loc32 = targetAddress - (fixupAddress + 6) + ref.addend(); + return; + case ripRel32Minus4: + *loc32 = targetAddress - (fixupAddress + 8) + ref.addend(); + return; + case delta32: + case delta32Anon: + *loc32 = targetAddress - fixupAddress + ref.addend(); + return; + case delta64: + case delta64Anon: + case unwindFDEToFunction: + *loc64 = targetAddress - fixupAddress + ref.addend(); + return; + case ripRel32GotLoadNowLea: + // Change MOVQ to LEA + assert(loc[-2] == 0x8B); + loc[-2] = 0x8D; + *loc32 = targetAddress - (fixupAddress + 4) + ref.addend(); + return; + case negDelta32: + *loc32 = fixupAddress - targetAddress + ref.addend(); + return; + case lazyPointer: + // Do nothing + return; + case lazyImmediateLocation: + *loc32 = ref.addend(); + return; + case imageOffset: + case imageOffsetGot: + *loc32 = (targetAddress - imageBaseAddress) + ref.addend(); + return; + case unwindInfoToEhFrame: { + uint64_t val = targetAddress - findSectionAddress(*ref.target()) + ref.addend(); + assert(val < 0xffffffU && "offset in __eh_frame too large"); + *loc32 = (*loc32 & 0xff000000U) | val; + return; + } + case invalid: + // Fall into llvm_unreachable(). + break; + } + llvm_unreachable("invalid x86_64 Reference Kind"); +} + + +void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref, + uint8_t *loc, + uint64_t fixupAddress, + uint64_t targetAddress, + uint64_t inAtomAddress) { + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) + return; + assert(ref.kindArch() == Reference::KindArch::x86_64); + ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc); + ulittle64_t *loc64 = reinterpret_cast<ulittle64_t *>(loc); + switch (static_cast<X86_64Kind>(ref.kindValue())) { + case branch32: + case ripRel32: + case ripRel32Got: + case ripRel32GotLoad: + *loc32 = ref.addend(); + return; + case ripRel32Anon: + *loc32 = (targetAddress - (fixupAddress + 4)) + ref.addend(); + return; + case pointer64: + *loc64 = ref.addend(); + return; + case pointer64Anon: + *loc64 = targetAddress + ref.addend(); + return; + case ripRel32Minus1: + *loc32 = ref.addend() - 1; + return; + case ripRel32Minus2: + *loc32 = ref.addend() - 2; + return; + case ripRel32Minus4: + *loc32 = ref.addend() - 4; + return; + case delta32: + *loc32 = ref.addend() + inAtomAddress - fixupAddress; + return; + case delta32Anon: + *loc32 = (targetAddress - fixupAddress) + ref.addend(); + return; + case delta64: + *loc64 = ref.addend() + inAtomAddress - fixupAddress; + return; + case delta64Anon: + *loc64 = (targetAddress - fixupAddress) + ref.addend(); + return; + case negDelta32: + *loc32 = fixupAddress - targetAddress + ref.addend(); + return; + case ripRel32GotLoadNowLea: + llvm_unreachable("ripRel32GotLoadNowLea implies GOT pass was run"); + return; + case lazyPointer: + case lazyImmediateLocation: + llvm_unreachable("lazy reference kind implies Stubs pass was run"); + return; + case imageOffset: + case imageOffsetGot: + case unwindInfoToEhFrame: + llvm_unreachable("fixup implies __unwind_info"); + return; + case unwindFDEToFunction: + // Do nothing for now + return; + case invalid: + // Fall into llvm_unreachable(). + break; + } + llvm_unreachable("unknown x86_64 Reference Kind"); +} + +void ArchHandler_x86_64::appendSectionRelocations( + const DefinedAtom &atom, + uint64_t atomSectionOffset, + const Reference &ref, + FindSymbolIndexForAtom symbolIndexForAtom, + FindSectionIndexForAtom sectionIndexForAtom, + FindAddressForAtom addressForAtom, + normalized::Relocations &relocs) { + if (ref.kindNamespace() != Reference::KindNamespace::mach_o) + return; + assert(ref.kindArch() == Reference::KindArch::x86_64); + uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom(); + switch (static_cast<X86_64Kind>(ref.kindValue())) { + case branch32: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + X86_64_RELOC_BRANCH | rPcRel | rExtern | rLength4); + return; + case ripRel32: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + X86_64_RELOC_SIGNED | rPcRel | rExtern | rLength4 ); + return; + case ripRel32Anon: + appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, + X86_64_RELOC_SIGNED | rPcRel | rLength4 ); + return; + case ripRel32Got: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + X86_64_RELOC_GOT | rPcRel | rExtern | rLength4 ); + return; + case ripRel32GotLoad: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + X86_64_RELOC_GOT_LOAD | rPcRel | rExtern | rLength4 ); + return; + case pointer64: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + X86_64_RELOC_UNSIGNED | rExtern | rLength8); + return; + case pointer64Anon: + appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, + X86_64_RELOC_UNSIGNED | rLength8); + return; + case ripRel32Minus1: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + X86_64_RELOC_SIGNED_1 | rPcRel | rExtern | rLength4 ); + return; + case ripRel32Minus2: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + X86_64_RELOC_SIGNED_2 | rPcRel | rExtern | rLength4 ); + return; + case ripRel32Minus4: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + X86_64_RELOC_SIGNED_4 | rPcRel | rExtern | rLength4 ); + return; + case delta32: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, + X86_64_RELOC_SUBTRACTOR | rExtern | rLength4 ); + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + X86_64_RELOC_UNSIGNED | rExtern | rLength4 ); + return; + case delta32Anon: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, + X86_64_RELOC_SUBTRACTOR | rExtern | rLength4 ); + appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, + X86_64_RELOC_UNSIGNED | rLength4 ); + return; + case delta64: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, + X86_64_RELOC_SUBTRACTOR | rExtern | rLength8 ); + appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, + X86_64_RELOC_UNSIGNED | rExtern | rLength8 ); + return; + case delta64Anon: + appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, + X86_64_RELOC_SUBTRACTOR | rExtern | rLength8 ); + appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, + X86_64_RELOC_UNSIGNED | rLength8 ); + return; + case unwindFDEToFunction: + case unwindInfoToEhFrame: + case negDelta32: + return; + case ripRel32GotLoadNowLea: + llvm_unreachable("ripRel32GotLoadNowLea implies GOT pass was run"); + return; + case lazyPointer: + case lazyImmediateLocation: + llvm_unreachable("lazy reference kind implies Stubs pass was run"); + return; + case imageOffset: + case imageOffsetGot: + llvm_unreachable("__unwind_info references should have been resolved"); + return; + case invalid: + // Fall into llvm_unreachable(). + break; + } + llvm_unreachable("unknown x86_64 Reference Kind"); +} + + +std::unique_ptr<mach_o::ArchHandler> ArchHandler::create_x86_64() { + return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_x86_64()); +} + +} // namespace mach_o +} // namespace lld diff --git a/lib/ReaderWriter/MachO/Atoms.h b/lib/ReaderWriter/MachO/Atoms.h new file mode 100644 index 0000000000000..8d60c1a163a68 --- /dev/null +++ b/lib/ReaderWriter/MachO/Atoms.h @@ -0,0 +1,181 @@ +//===- lib/ReaderWriter/MachO/Atoms.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_ATOMS_H +#define LLD_READER_WRITER_MACHO_ATOMS_H + +#include "lld/Core/Simple.h" + +namespace lld { +namespace mach_o { +class MachODefinedAtom : public SimpleDefinedAtom { +public: + MachODefinedAtom(const File &f, const StringRef name, Scope scope, + ContentType type, Merge merge, bool thumb, bool noDeadStrip, + const ArrayRef<uint8_t> content, Alignment align) + : SimpleDefinedAtom(f), _name(name), _content(content), + _align(align), _contentType(type), _scope(scope), _merge(merge), + _thumb(thumb), _noDeadStrip(noDeadStrip) {} + + // Constructor for zero-fill content + MachODefinedAtom(const File &f, const StringRef name, Scope scope, + uint64_t size, bool noDeadStrip, Alignment align) + : SimpleDefinedAtom(f), _name(name), + _content(ArrayRef<uint8_t>(nullptr, size)), _align(align), + _contentType(DefinedAtom::typeZeroFill), + _scope(scope), _merge(mergeNo), _thumb(false), + _noDeadStrip(noDeadStrip) {} + + uint64_t size() const override { return _content.size(); } + + ContentType contentType() const override { return _contentType; } + + Alignment alignment() const override { return _align; } + + StringRef name() const override { return _name; } + + Scope scope() const override { return _scope; } + + Merge merge() const override { return _merge; } + + DeadStripKind deadStrip() const override { + if (_contentType == DefinedAtom::typeInitializerPtr) + return deadStripNever; + if (_contentType == DefinedAtom::typeTerminatorPtr) + return deadStripNever; + if (_noDeadStrip) + return deadStripNever; + return deadStripNormal; + } + + ArrayRef<uint8_t> rawContent() const override { + // Note: Zerofill atoms have a content pointer which is null. + return _content; + } + + bool isThumb() const { return _thumb; } + + void addReference(uint32_t offsetInAtom, uint16_t relocType, + const Atom *target, Reference::Addend addend, + Reference::KindArch arch = Reference::KindArch::x86_64, + Reference::KindNamespace ns + = Reference::KindNamespace::mach_o) { + SimpleDefinedAtom::addReference(ns, arch, relocType, offsetInAtom, target, + addend); + } + +private: + const StringRef _name; + const ArrayRef<uint8_t> _content; + const DefinedAtom::Alignment _align; + const ContentType _contentType; + const Scope _scope; + const Merge _merge; + const bool _thumb; + const bool _noDeadStrip; +}; + +class MachODefinedCustomSectionAtom : public MachODefinedAtom { +public: + MachODefinedCustomSectionAtom(const File &f, const StringRef name, + Scope scope, ContentType type, Merge merge, + bool thumb, bool noDeadStrip, + const ArrayRef<uint8_t> content, + StringRef sectionName, Alignment align) + : MachODefinedAtom(f, name, scope, type, merge, thumb, noDeadStrip, + content, align), + _sectionName(sectionName) {} + + SectionChoice sectionChoice() const override { + return DefinedAtom::sectionCustomRequired; + } + + StringRef customSectionName() const override { + return _sectionName; + } +private: + StringRef _sectionName; +}; + + +class MachOTentativeDefAtom : public SimpleDefinedAtom { +public: + MachOTentativeDefAtom(const File &f, const StringRef name, Scope scope, + uint64_t size, DefinedAtom::Alignment align) + : SimpleDefinedAtom(f), _name(name), _scope(scope), _size(size), + _align(align) {} + + uint64_t size() const override { return _size; } + + Merge merge() const override { return DefinedAtom::mergeAsTentative; } + + ContentType contentType() const override { return DefinedAtom::typeZeroFill; } + + Alignment alignment() const override { return _align; } + + StringRef name() const override { return _name; } + + Scope scope() const override { return _scope; } + + ArrayRef<uint8_t> rawContent() const override { return ArrayRef<uint8_t>(); } + +private: + const StringRef _name; + const Scope _scope; + const uint64_t _size; + const DefinedAtom::Alignment _align; +}; + +class MachOSharedLibraryAtom : public SharedLibraryAtom { +public: + MachOSharedLibraryAtom(const File &file, StringRef name, + StringRef dylibInstallName, bool weakDef) + : SharedLibraryAtom(), _file(file), _name(name), + _dylibInstallName(dylibInstallName) {} + virtual ~MachOSharedLibraryAtom() {} + + virtual StringRef loadName() const override { + return _dylibInstallName; + } + + virtual bool canBeNullAtRuntime() const override { + // FIXME: this may actually be changeable. For now, all symbols are strongly + // defined though. + return false; + } + + virtual const File& file() const override { + return _file; + } + + virtual StringRef name() const override { + return _name; + } + + virtual Type type() const override { + // Unused in MachO (I think). + return Type::Unknown; + } + + virtual uint64_t size() const override { + // Unused in MachO (I think) + return 0; + } + +private: + const File &_file; + StringRef _name; + StringRef _dylibInstallName; +}; + + +} // mach_o +} // lld + +#endif diff --git a/lib/ReaderWriter/MachO/CMakeLists.txt b/lib/ReaderWriter/MachO/CMakeLists.txt new file mode 100644 index 0000000000000..e396537c63c82 --- /dev/null +++ b/lib/ReaderWriter/MachO/CMakeLists.txt @@ -0,0 +1,26 @@ +add_llvm_library(lldMachO + ArchHandler.cpp + ArchHandler_arm.cpp + ArchHandler_arm64.cpp + ArchHandler_x86.cpp + ArchHandler_x86_64.cpp + CompactUnwindPass.cpp + GOTPass.cpp + LayoutPass.cpp + MachOLinkingContext.cpp + MachONormalizedFileBinaryReader.cpp + MachONormalizedFileBinaryWriter.cpp + MachONormalizedFileFromAtoms.cpp + MachONormalizedFileToAtoms.cpp + MachONormalizedFileYAML.cpp + ShimPass.cpp + StubsPass.cpp + WriterMachO.cpp + LINK_LIBS + lldCore + lldYAML + LLVMObject + LLVMSupport + ) + +include_directories(.) diff --git a/lib/ReaderWriter/MachO/CompactUnwindPass.cpp b/lib/ReaderWriter/MachO/CompactUnwindPass.cpp new file mode 100644 index 0000000000000..fc8608383e5da --- /dev/null +++ b/lib/ReaderWriter/MachO/CompactUnwindPass.cpp @@ -0,0 +1,530 @@ +//===- lib/ReaderWriter/MachO/CompactUnwindPass.cpp -----------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file A pass to convert MachO's __compact_unwind sections into the final +/// __unwind_info format used during runtime. See +/// mach-o/compact_unwind_encoding.h for more details on the formats involved. +/// +//===----------------------------------------------------------------------===// + +#include "ArchHandler.h" +#include "File.h" +#include "MachONormalizedFileBinaryUtils.h" +#include "MachOPasses.h" +#include "lld/Core/DefinedAtom.h" +#include "lld/Core/File.h" +#include "lld/Core/LLVM.h" +#include "lld/Core/Reference.h" +#include "lld/Core/Simple.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Format.h" + +#define DEBUG_TYPE "macho-compact-unwind" + +namespace lld { +namespace mach_o { + +namespace { +struct CompactUnwindEntry { + const Atom *rangeStart; + const Atom *personalityFunction; + const Atom *lsdaLocation; + const Atom *ehFrame; + + uint32_t rangeLength; + + // There are 3 types of compact unwind entry, distinguished by the encoding + // value: 0 indicates a function with no unwind info; + // _archHandler.dwarfCompactUnwindType() indicates that the entry defers to + // __eh_frame, and that the ehFrame entry will be valid; any other value is a + // real compact unwind entry -- personalityFunction will be set and + // lsdaLocation may be. + uint32_t encoding; + + CompactUnwindEntry(const DefinedAtom *function) + : rangeStart(function), personalityFunction(nullptr), + lsdaLocation(nullptr), ehFrame(nullptr), rangeLength(function->size()), + encoding(0) {} + + CompactUnwindEntry() + : rangeStart(nullptr), personalityFunction(nullptr), + lsdaLocation(nullptr), ehFrame(nullptr), rangeLength(0), encoding(0) {} +}; + +struct UnwindInfoPage { + std::vector<CompactUnwindEntry> entries; +}; +} + +class UnwindInfoAtom : public SimpleDefinedAtom { +public: + UnwindInfoAtom(ArchHandler &archHandler, const File &file, bool isBig, + std::vector<const Atom *> &personalities, + std::vector<uint32_t> &commonEncodings, + std::vector<UnwindInfoPage> &pages, uint32_t numLSDAs) + : SimpleDefinedAtom(file), _archHandler(archHandler), + _commonEncodingsOffset(7 * sizeof(uint32_t)), + _personalityArrayOffset(_commonEncodingsOffset + + commonEncodings.size() * sizeof(uint32_t)), + _topLevelIndexOffset(_personalityArrayOffset + + personalities.size() * sizeof(uint32_t)), + _lsdaIndexOffset(_topLevelIndexOffset + + 3 * (pages.size() + 1) * sizeof(uint32_t)), + _firstPageOffset(_lsdaIndexOffset + 2 * numLSDAs * sizeof(uint32_t)), + _isBig(isBig) { + + addHeader(commonEncodings.size(), personalities.size(), pages.size()); + addCommonEncodings(commonEncodings); + addPersonalityFunctions(personalities); + addTopLevelIndexes(pages); + addLSDAIndexes(pages, numLSDAs); + addSecondLevelPages(pages); + } + + ContentType contentType() const override { + return DefinedAtom::typeProcessedUnwindInfo; + } + + Alignment alignment() const override { return Alignment(2); } + + uint64_t size() const override { return _contents.size(); } + + ContentPermissions permissions() const override { + return DefinedAtom::permR__; + } + + ArrayRef<uint8_t> rawContent() const override { return _contents; } + + void addHeader(uint32_t numCommon, uint32_t numPersonalities, + uint32_t numPages) { + using normalized::write32; + + uint32_t headerSize = 7 * sizeof(uint32_t); + _contents.resize(headerSize); + + uint8_t *headerEntries = _contents.data(); + // version + write32(headerEntries, 1, _isBig); + // commonEncodingsArraySectionOffset + write32(headerEntries + sizeof(uint32_t), _commonEncodingsOffset, _isBig); + // commonEncodingsArrayCount + write32(headerEntries + 2 * sizeof(uint32_t), numCommon, _isBig); + // personalityArraySectionOffset + write32(headerEntries + 3 * sizeof(uint32_t), _personalityArrayOffset, + _isBig); + // personalityArrayCount + write32(headerEntries + 4 * sizeof(uint32_t), numPersonalities, _isBig); + // indexSectionOffset + write32(headerEntries + 5 * sizeof(uint32_t), _topLevelIndexOffset, _isBig); + // indexCount + write32(headerEntries + 6 * sizeof(uint32_t), numPages + 1, _isBig); + } + + /// Add the list of common encodings to the section; this is simply an array + /// of uint32_t compact values. Size has already been specified in the header. + void addCommonEncodings(std::vector<uint32_t> &commonEncodings) { + using normalized::write32; + + _contents.resize(_commonEncodingsOffset + + commonEncodings.size() * sizeof(uint32_t)); + uint8_t *commonEncodingsArea = + reinterpret_cast<uint8_t *>(_contents.data() + _commonEncodingsOffset); + + for (uint32_t encoding : commonEncodings) { + write32(commonEncodingsArea, encoding, _isBig); + commonEncodingsArea += sizeof(uint32_t); + } + } + + void addPersonalityFunctions(std::vector<const Atom *> personalities) { + _contents.resize(_personalityArrayOffset + + personalities.size() * sizeof(uint32_t)); + + for (unsigned i = 0; i < personalities.size(); ++i) + addImageReferenceIndirect(_personalityArrayOffset + i * sizeof(uint32_t), + personalities[i]); + } + + void addTopLevelIndexes(std::vector<UnwindInfoPage> &pages) { + using normalized::write32; + + uint32_t numIndexes = pages.size() + 1; + _contents.resize(_topLevelIndexOffset + numIndexes * 3 * sizeof(uint32_t)); + + uint32_t pageLoc = _firstPageOffset; + + // The most difficult job here is calculating the LSDAs; everything else + // follows fairly naturally, but we can't state where the first + uint8_t *indexData = &_contents[_topLevelIndexOffset]; + uint32_t numLSDAs = 0; + for (unsigned i = 0; i < pages.size(); ++i) { + // functionOffset + addImageReference(_topLevelIndexOffset + 3 * i * sizeof(uint32_t), + pages[i].entries[0].rangeStart); + // secondLevelPagesSectionOffset + write32(indexData + (3 * i + 1) * sizeof(uint32_t), pageLoc, _isBig); + write32(indexData + (3 * i + 2) * sizeof(uint32_t), + _lsdaIndexOffset + numLSDAs * 2 * sizeof(uint32_t), _isBig); + + for (auto &entry : pages[i].entries) + if (entry.lsdaLocation) + ++numLSDAs; + } + + // Finally, write out the final sentinel index + CompactUnwindEntry &finalEntry = pages[pages.size() - 1].entries.back(); + addImageReference(_topLevelIndexOffset + + 3 * pages.size() * sizeof(uint32_t), + finalEntry.rangeStart, finalEntry.rangeLength); + // secondLevelPagesSectionOffset => 0 + write32(indexData + (3 * pages.size() + 2) * sizeof(uint32_t), + _lsdaIndexOffset + numLSDAs * 2 * sizeof(uint32_t), _isBig); + } + + void addLSDAIndexes(std::vector<UnwindInfoPage> &pages, uint32_t numLSDAs) { + _contents.resize(_lsdaIndexOffset + numLSDAs * 2 * sizeof(uint32_t)); + + uint32_t curOffset = _lsdaIndexOffset; + for (auto &page : pages) { + for (auto &entry : page.entries) { + if (!entry.lsdaLocation) + continue; + + addImageReference(curOffset, entry.rangeStart); + addImageReference(curOffset + sizeof(uint32_t), entry.lsdaLocation); + curOffset += 2 * sizeof(uint32_t); + } + } + } + + void addSecondLevelPages(std::vector<UnwindInfoPage> &pages) { + for (auto &page : pages) { + addRegularSecondLevelPage(page); + } + } + + void addRegularSecondLevelPage(const UnwindInfoPage &page) { + uint32_t curPageOffset = _contents.size(); + const int16_t headerSize = sizeof(uint32_t) + 2 * sizeof(uint16_t); + uint32_t curPageSize = + headerSize + 2 * page.entries.size() * sizeof(uint32_t); + _contents.resize(curPageOffset + curPageSize); + + using normalized::write32; + using normalized::write16; + // 2 => regular page + write32(&_contents[curPageOffset], 2, _isBig); + // offset of 1st entry + write16(&_contents[curPageOffset + 4], headerSize, _isBig); + write16(&_contents[curPageOffset + 6], page.entries.size(), _isBig); + + uint32_t pagePos = curPageOffset + headerSize; + for (auto &entry : page.entries) { + addImageReference(pagePos, entry.rangeStart); + + write32(_contents.data() + pagePos + sizeof(uint32_t), entry.encoding, + _isBig); + if ((entry.encoding & 0x0f000000U) == + _archHandler.dwarfCompactUnwindType()) + addEhFrameReference(pagePos + sizeof(uint32_t), entry.ehFrame); + + pagePos += 2 * sizeof(uint32_t); + } + } + + void addEhFrameReference(uint32_t offset, const Atom *dest, + Reference::Addend addend = 0) { + addReference(Reference::KindNamespace::mach_o, _archHandler.kindArch(), + _archHandler.unwindRefToEhFrameKind(), offset, dest, addend); + } + + void addImageReference(uint32_t offset, const Atom *dest, + Reference::Addend addend = 0) { + addReference(Reference::KindNamespace::mach_o, _archHandler.kindArch(), + _archHandler.imageOffsetKind(), offset, dest, addend); + } + + void addImageReferenceIndirect(uint32_t offset, const Atom *dest) { + addReference(Reference::KindNamespace::mach_o, _archHandler.kindArch(), + _archHandler.imageOffsetKindIndirect(), offset, dest, 0); + } + +private: + mach_o::ArchHandler &_archHandler; + std::vector<uint8_t> _contents; + uint32_t _commonEncodingsOffset; + uint32_t _personalityArrayOffset; + uint32_t _topLevelIndexOffset; + uint32_t _lsdaIndexOffset; + uint32_t _firstPageOffset; + bool _isBig; +}; + +/// Pass for instantiating and optimizing GOT slots. +/// +class CompactUnwindPass : public Pass { +public: + CompactUnwindPass(const MachOLinkingContext &context) + : _context(context), _archHandler(_context.archHandler()), + _file("<mach-o Compact Unwind Pass>"), + _isBig(MachOLinkingContext::isBigEndian(_context.arch())) {} + +private: + void perform(std::unique_ptr<MutableFile> &mergedFile) override { + DEBUG(llvm::dbgs() << "MachO Compact Unwind pass\n"); + + std::map<const Atom *, CompactUnwindEntry> unwindLocs; + std::map<const Atom *, const Atom *> dwarfFrames; + std::vector<const Atom *> personalities; + uint32_t numLSDAs = 0; + + // First collect all __compact_unwind and __eh_frame entries, addressable by + // the function referred to. + collectCompactUnwindEntries(mergedFile, unwindLocs, personalities, + numLSDAs); + + collectDwarfFrameEntries(mergedFile, dwarfFrames); + + // Skip rest of pass if no unwind info. + if (unwindLocs.empty() && dwarfFrames.empty()) + return; + + // FIXME: if there are more than 4 personality functions then we need to + // defer to DWARF info for the ones we don't put in the list. They should + // also probably be sorted by frequency. + assert(personalities.size() <= 4); + + // TODO: Find commmon encodings for use by compressed pages. + std::vector<uint32_t> commonEncodings; + + // Now sort the entries by final address and fixup the compact encoding to + // its final form (i.e. set personality function bits & create DWARF + // references where needed). + std::vector<CompactUnwindEntry> unwindInfos = createUnwindInfoEntries( + mergedFile, unwindLocs, personalities, dwarfFrames); + + // Finally, we can start creating pages based on these entries. + + DEBUG(llvm::dbgs() << " Splitting entries into pages\n"); + // FIXME: we split the entries into pages naively: lots of 4k pages followed + // by a small one. ld64 tried to minimize space and align them to real 4k + // boundaries. That might be worth doing, or perhaps we could perform some + // minor balancing for expected number of lookups. + std::vector<UnwindInfoPage> pages; + unsigned pageStart = 0; + do { + pages.push_back(UnwindInfoPage()); + + // FIXME: we only create regular pages at the moment. These can hold up to + // 1021 entries according to the documentation. + unsigned entriesInPage = + std::min(1021U, (unsigned)unwindInfos.size() - pageStart); + + std::copy(unwindInfos.begin() + pageStart, + unwindInfos.begin() + pageStart + entriesInPage, + std::back_inserter(pages.back().entries)); + pageStart += entriesInPage; + + DEBUG(llvm::dbgs() + << " Page from " << pages.back().entries[0].rangeStart->name() + << " to " << pages.back().entries.back().rangeStart->name() << " + " + << llvm::format("0x%x", pages.back().entries.back().rangeLength) + << " has " << entriesInPage << " entries\n"); + } while (pageStart < unwindInfos.size()); + + UnwindInfoAtom *unwind = new (_file.allocator()) + UnwindInfoAtom(_archHandler, _file, _isBig, personalities, + commonEncodings, pages, numLSDAs); + mergedFile->addAtom(*unwind); + + // Finally, remove all __compact_unwind atoms now that we've processed them. + mergedFile->removeDefinedAtomsIf([](const DefinedAtom *atom) { + return atom->contentType() == DefinedAtom::typeCompactUnwindInfo; + }); + } + + void collectCompactUnwindEntries( + std::unique_ptr<MutableFile> &mergedFile, + std::map<const Atom *, CompactUnwindEntry> &unwindLocs, + std::vector<const Atom *> &personalities, uint32_t &numLSDAs) { + DEBUG(llvm::dbgs() << " Collecting __compact_unwind entries\n"); + + for (const DefinedAtom *atom : mergedFile->defined()) { + if (atom->contentType() != DefinedAtom::typeCompactUnwindInfo) + continue; + + auto unwindEntry = extractCompactUnwindEntry(atom); + unwindLocs.insert(std::make_pair(unwindEntry.rangeStart, unwindEntry)); + + DEBUG(llvm::dbgs() << " Entry for " << unwindEntry.rangeStart->name() + << ", encoding=" + << llvm::format("0x%08x", unwindEntry.encoding)); + if (unwindEntry.personalityFunction) + DEBUG(llvm::dbgs() << ", personality=" + << unwindEntry.personalityFunction->name() + << ", lsdaLoc=" << unwindEntry.lsdaLocation->name()); + DEBUG(llvm::dbgs() << '\n'); + + // Count number of LSDAs we see, since we need to know how big the index + // will be while laying out the section. + if (unwindEntry.lsdaLocation) + ++numLSDAs; + + // Gather the personality functions now, so that they're in deterministic + // order (derived from the DefinedAtom order). + if (unwindEntry.personalityFunction) { + auto pFunc = std::find(personalities.begin(), personalities.end(), + unwindEntry.personalityFunction); + if (pFunc == personalities.end()) + personalities.push_back(unwindEntry.personalityFunction); + } + } + } + + CompactUnwindEntry extractCompactUnwindEntry(const DefinedAtom *atom) { + CompactUnwindEntry entry; + + for (const Reference *ref : *atom) { + switch (ref->offsetInAtom()) { + case 0: + // FIXME: there could legitimately be functions with multiple encoding + // entries. However, nothing produces them at the moment. + assert(ref->addend() == 0 && "unexpected offset into function"); + entry.rangeStart = ref->target(); + break; + case 0x10: + assert(ref->addend() == 0 && "unexpected offset into personality fn"); + entry.personalityFunction = ref->target(); + break; + case 0x18: + assert(ref->addend() == 0 && "unexpected offset into LSDA atom"); + entry.lsdaLocation = ref->target(); + break; + } + } + + if (atom->rawContent().size() < 4 * sizeof(uint32_t)) + return entry; + + using normalized::read32; + entry.rangeLength = + read32(atom->rawContent().data() + 2 * sizeof(uint32_t), _isBig); + entry.encoding = + read32(atom->rawContent().data() + 3 * sizeof(uint32_t), _isBig); + return entry; + } + + void + collectDwarfFrameEntries(std::unique_ptr<MutableFile> &mergedFile, + std::map<const Atom *, const Atom *> &dwarfFrames) { + for (const DefinedAtom *ehFrameAtom : mergedFile->defined()) { + if (ehFrameAtom->contentType() != DefinedAtom::typeCFI) + continue; + if (ArchHandler::isDwarfCIE(_isBig, ehFrameAtom)) + continue; + + if (const Atom *function = _archHandler.fdeTargetFunction(ehFrameAtom)) + dwarfFrames[function] = ehFrameAtom; + } + } + + /// Every atom defined in __TEXT,__text needs an entry in the final + /// __unwind_info section (in order). These comes from two sources: + /// + Input __compact_unwind sections where possible (after adding the + /// personality function offset which is only known now). + /// + A synthesised reference to __eh_frame if there's no __compact_unwind + /// or too many personality functions to be accommodated. + std::vector<CompactUnwindEntry> createUnwindInfoEntries( + const std::unique_ptr<MutableFile> &mergedFile, + const std::map<const Atom *, CompactUnwindEntry> &unwindLocs, + const std::vector<const Atom *> &personalities, + const std::map<const Atom *, const Atom *> &dwarfFrames) { + std::vector<CompactUnwindEntry> unwindInfos; + + DEBUG(llvm::dbgs() << " Creating __unwind_info entries\n"); + // The final order in the __unwind_info section must be derived from the + // order of typeCode atoms, since that's how they'll be put into the object + // file eventually (yuck!). + for (const DefinedAtom *atom : mergedFile->defined()) { + if (atom->contentType() != DefinedAtom::typeCode) + continue; + + unwindInfos.push_back(finalizeUnwindInfoEntryForAtom( + atom, unwindLocs, personalities, dwarfFrames)); + + DEBUG(llvm::dbgs() << " Entry for " << atom->name() + << ", final encoding=" + << llvm::format("0x%08x", unwindInfos.back().encoding) + << '\n'); + } + + return unwindInfos; + } + + CompactUnwindEntry finalizeUnwindInfoEntryForAtom( + const DefinedAtom *function, + const std::map<const Atom *, CompactUnwindEntry> &unwindLocs, + const std::vector<const Atom *> &personalities, + const std::map<const Atom *, const Atom *> &dwarfFrames) { + auto unwindLoc = unwindLocs.find(function); + + CompactUnwindEntry entry; + if (unwindLoc == unwindLocs.end()) { + // Default entry has correct encoding (0 => no unwind), but we need to + // synthesise the function. + entry.rangeStart = function; + entry.rangeLength = function->size(); + } else + entry = unwindLoc->second; + + + // If there's no __compact_unwind entry, or it explicitly says to use + // __eh_frame, we need to try and fill in the correct DWARF atom. + if (entry.encoding == _archHandler.dwarfCompactUnwindType() || + entry.encoding == 0) { + auto dwarfFrame = dwarfFrames.find(function); + if (dwarfFrame != dwarfFrames.end()) { + entry.encoding = _archHandler.dwarfCompactUnwindType(); + entry.ehFrame = dwarfFrame->second; + } + } + + + auto personality = std::find(personalities.begin(), personalities.end(), + entry.personalityFunction); + uint32_t personalityIdx = personality == personalities.end() + ? 0 + : personality - personalities.begin() + 1; + + // FIXME: We should also use DWARF when there isn't enough room for the + // personality function in the compact encoding. + assert(personalityIdx < 4 && "too many personality functions"); + + entry.encoding |= personalityIdx << 28; + + if (entry.lsdaLocation) + entry.encoding |= 1U << 30; + + return entry; + } + + const MachOLinkingContext &_context; + mach_o::ArchHandler &_archHandler; + MachOFile _file; + bool _isBig; +}; + +void addCompactUnwindPass(PassManager &pm, const MachOLinkingContext &ctx) { + assert(ctx.needsCompactUnwindPass()); + pm.add(llvm::make_unique<CompactUnwindPass>(ctx)); +} + +} // end namesapce mach_o +} // end namesapce lld diff --git a/lib/ReaderWriter/MachO/ExecutableAtoms.hpp b/lib/ReaderWriter/MachO/ExecutableAtoms.hpp new file mode 100644 index 0000000000000..cd562de216d9e --- /dev/null +++ b/lib/ReaderWriter/MachO/ExecutableAtoms.hpp @@ -0,0 +1,136 @@ +//===- lib/ReaderWriter/MachO/ExecutableAtoms.hpp -------------------------===// +// +// 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_EXECUTABLE_ATOMS_H +#define LLD_READER_WRITER_MACHO_EXECUTABLE_ATOMS_H + +#include "Atoms.h" + +#include "llvm/Support/MachO.h" + +#include "lld/Core/ArchiveLibraryFile.h" +#include "lld/Core/DefinedAtom.h" +#include "lld/Core/File.h" +#include "lld/Core/LinkingContext.h" +#include "lld/Core/Reference.h" +#include "lld/Core/Simple.h" +#include "lld/Core/UndefinedAtom.h" +#include "lld/ReaderWriter/MachOLinkingContext.h" + +namespace lld { +namespace mach_o { + + +// +// CEntryFile adds an UndefinedAtom for "_main" so that the Resolving +// phase will fail if "_main" is undefined. +// +class CEntryFile : public SimpleFile { +public: + CEntryFile(const MachOLinkingContext &context) + : SimpleFile("C entry"), + _undefMain(*this, context.entrySymbolName()) { + this->addAtom(_undefMain); + } + +private: + SimpleUndefinedAtom _undefMain; +}; + + +// +// StubHelperFile adds an UndefinedAtom for "dyld_stub_binder" so that +// the Resolveing phase will fail if "dyld_stub_binder" is undefined. +// +class StubHelperFile : public SimpleFile { +public: + StubHelperFile(const MachOLinkingContext &context) + : SimpleFile("stub runtime"), + _undefBinder(*this, context.binderSymbolName()) { + this->addAtom(_undefBinder); + } + +private: + SimpleUndefinedAtom _undefBinder; +}; + + +// +// MachHeaderAliasFile lazily instantiates the magic symbols that mark the start +// of the mach_header for final linked images. +// +class MachHeaderAliasFile : public ArchiveLibraryFile { +public: + MachHeaderAliasFile(const MachOLinkingContext &context) + : ArchiveLibraryFile("mach_header symbols") { + switch (context.outputMachOType()) { + case llvm::MachO::MH_EXECUTE: + _machHeaderSymbolName = "__mh_execute_header"; + break; + case llvm::MachO::MH_DYLIB: + _machHeaderSymbolName = "__mh_dylib_header"; + break; + case llvm::MachO::MH_BUNDLE: + _machHeaderSymbolName = "__mh_bundle_header"; + break; + case llvm::MachO::MH_DYLINKER: + _machHeaderSymbolName = "__mh_dylinker_header"; + break; + case llvm::MachO::MH_PRELOAD: + _machHeaderSymbolName = "__mh_preload_header"; + break; + default: + llvm_unreachable("no mach_header symbol for file type"); + } + } + + std::error_code + parseAllMembers(std::vector<std::unique_ptr<File>> &result) override { + return std::error_code(); + } + + File *find(StringRef sym, bool dataSymbolOnly) override { + if (sym.equals("___dso_handle") || sym.equals(_machHeaderSymbolName)) { + _definedAtoms._atoms.push_back(new (allocator()) MachODefinedAtom( + *this, sym, DefinedAtom::scopeLinkageUnit, + DefinedAtom::typeMachHeader, DefinedAtom::mergeNo, false, false, + ArrayRef<uint8_t>(), DefinedAtom::Alignment(12,0))); + return this; + } + return nullptr; + } + + 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; + } + + +private: + mutable atom_collection_vector<DefinedAtom> _definedAtoms; + atom_collection_vector<UndefinedAtom> _undefinedAtoms; + atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms; + atom_collection_vector<AbsoluteAtom> _absoluteAtoms; + StringRef _machHeaderSymbolName; +}; + +} // namespace mach_o +} // namespace lld + +#endif // LLD_READER_WRITER_MACHO_EXECUTABLE_ATOMS_H diff --git a/lib/ReaderWriter/MachO/File.h b/lib/ReaderWriter/MachO/File.h new file mode 100644 index 0000000000000..913644ec1fc02 --- /dev/null +++ b/lib/ReaderWriter/MachO/File.h @@ -0,0 +1,327 @@ +//===- lib/ReaderWriter/MachO/File.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_FILE_H +#define LLD_READER_WRITER_MACHO_FILE_H + +#include "Atoms.h" +#include "MachONormalizedFile.h" +#include "lld/Core/SharedLibraryFile.h" +#include "lld/Core/Simple.h" +#include "llvm/ADT/StringMap.h" +#include <unordered_map> + +namespace lld { +namespace mach_o { + +using lld::mach_o::normalized::Section; + +class MachOFile : public SimpleFile { +public: + MachOFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx) + : SimpleFile(mb->getBufferIdentifier()), _mb(std::move(mb)), _ctx(ctx) {} + + MachOFile(StringRef path) : SimpleFile(path) {} + + void addDefinedAtom(StringRef name, Atom::Scope scope, + DefinedAtom::ContentType type, DefinedAtom::Merge merge, + uint64_t sectionOffset, uint64_t contentSize, bool thumb, + bool noDeadStrip, bool copyRefs, + const Section *inSection) { + assert(sectionOffset+contentSize <= inSection->content.size()); + ArrayRef<uint8_t> content = inSection->content.slice(sectionOffset, + contentSize); + if (copyRefs) { + // Make a copy of the atom's name and content that is owned by this file. + name = name.copy(allocator()); + content = content.copy(allocator()); + } + DefinedAtom::Alignment align( + inSection->alignment, + sectionOffset % ((uint64_t)1 << inSection->alignment)); + MachODefinedAtom *atom = + new (allocator()) MachODefinedAtom(*this, name, scope, type, merge, + thumb, noDeadStrip, content, align); + addAtomForSection(inSection, atom, sectionOffset); + } + + void addDefinedAtomInCustomSection(StringRef name, Atom::Scope scope, + DefinedAtom::ContentType type, DefinedAtom::Merge merge, + bool thumb, bool noDeadStrip, uint64_t sectionOffset, + uint64_t contentSize, StringRef sectionName, + bool copyRefs, const Section *inSection) { + assert(sectionOffset+contentSize <= inSection->content.size()); + ArrayRef<uint8_t> content = inSection->content.slice(sectionOffset, + contentSize); + if (copyRefs) { + // Make a copy of the atom's name and content that is owned by this file. + name = name.copy(allocator()); + content = content.copy(allocator()); + sectionName = sectionName.copy(allocator()); + } + DefinedAtom::Alignment align( + inSection->alignment, + sectionOffset % ((uint64_t)1 << inSection->alignment)); + MachODefinedCustomSectionAtom *atom = + new (allocator()) MachODefinedCustomSectionAtom(*this, name, scope, type, + merge, thumb, + noDeadStrip, content, + sectionName, align); + addAtomForSection(inSection, atom, sectionOffset); + } + + void addZeroFillDefinedAtom(StringRef name, Atom::Scope scope, + uint64_t sectionOffset, uint64_t size, + bool noDeadStrip, bool copyRefs, + const Section *inSection) { + if (copyRefs) { + // Make a copy of the atom's name and content that is owned by this file. + name = name.copy(allocator()); + } + DefinedAtom::Alignment align( + inSection->alignment, + sectionOffset % ((uint64_t)1 << inSection->alignment)); + MachODefinedAtom *atom = + new (allocator()) MachODefinedAtom(*this, name, scope, size, noDeadStrip, + align); + addAtomForSection(inSection, atom, sectionOffset); + } + + void addUndefinedAtom(StringRef name, bool copyRefs) { + if (copyRefs) { + // Make a copy of the atom's name that is owned by this file. + name = name.copy(allocator()); + } + SimpleUndefinedAtom *atom = + new (allocator()) SimpleUndefinedAtom(*this, name); + addAtom(*atom); + _undefAtoms[name] = atom; + } + + void addTentativeDefAtom(StringRef name, Atom::Scope scope, uint64_t size, + DefinedAtom::Alignment align, bool copyRefs) { + if (copyRefs) { + // Make a copy of the atom's name that is owned by this file. + name = name.copy(allocator()); + } + MachOTentativeDefAtom *atom = + new (allocator()) MachOTentativeDefAtom(*this, name, scope, size, align); + addAtom(*atom); + _undefAtoms[name] = atom; + } + + /// Search this file for an the atom from 'section' that covers + /// 'offsetInSect'. Returns nullptr is no atom found. + MachODefinedAtom *findAtomCoveringAddress(const Section §ion, + uint64_t offsetInSect, + uint32_t *foundOffsetAtom=nullptr) { + const auto &pos = _sectionAtoms.find(§ion); + if (pos == _sectionAtoms.end()) + return nullptr; + const auto &vec = pos->second; + assert(offsetInSect < section.content.size()); + // Vector of atoms for section are already sorted, so do binary search. + const auto &atomPos = std::lower_bound(vec.begin(), vec.end(), offsetInSect, + [offsetInSect](const SectionOffsetAndAtom &ao, + uint64_t targetAddr) -> bool { + // Each atom has a start offset of its slice of the + // section's content. This compare function must return true + // iff the atom's range is before the offset being searched for. + uint64_t atomsEndOffset = ao.offset+ao.atom->rawContent().size(); + return (atomsEndOffset <= offsetInSect); + }); + if (atomPos == vec.end()) + return nullptr; + if (foundOffsetAtom) + *foundOffsetAtom = offsetInSect - atomPos->offset; + return atomPos->atom; + } + + /// Searches this file for an UndefinedAtom named 'name'. Returns + /// nullptr is no such atom found. + const lld::Atom *findUndefAtom(StringRef name) { + auto pos = _undefAtoms.find(name); + if (pos == _undefAtoms.end()) + return nullptr; + return pos->second; + } + + typedef std::function<void (MachODefinedAtom* atom)> DefinedAtomVisitor; + + void eachDefinedAtom(DefinedAtomVisitor vistor) { + for (auto §AndAtoms : _sectionAtoms) { + for (auto &offAndAtom : sectAndAtoms.second) { + vistor(offAndAtom.atom); + } + } + } + + typedef std::function<void(MachODefinedAtom *atom, uint64_t offset)> + SectionAtomVisitor; + + void eachAtomInSection(const Section §ion, SectionAtomVisitor visitor) { + auto pos = _sectionAtoms.find(§ion); + if (pos == _sectionAtoms.end()) + return; + auto vec = pos->second; + + for (auto &offAndAtom : vec) + visitor(offAndAtom.atom, offAndAtom.offset); + } + +protected: + std::error_code doParse() override { + // Convert binary file to normalized mach-o. + auto normFile = normalized::readBinary(_mb, _ctx->arch()); + if (std::error_code ec = normFile.getError()) + return ec; + // Convert normalized mach-o to atoms. + if (std::error_code ec = normalized::normalizedObjectToAtoms( + this, **normFile, false)) + return ec; + return std::error_code(); + } + +private: + struct SectionOffsetAndAtom { uint64_t offset; MachODefinedAtom *atom; }; + + void addAtomForSection(const Section *inSection, MachODefinedAtom* atom, + uint64_t sectionOffset) { + SectionOffsetAndAtom offAndAtom; + offAndAtom.offset = sectionOffset; + offAndAtom.atom = atom; + _sectionAtoms[inSection].push_back(offAndAtom); + addAtom(*atom); + } + + + typedef llvm::DenseMap<const normalized::Section *, + std::vector<SectionOffsetAndAtom>> SectionToAtoms; + typedef llvm::StringMap<const lld::Atom *> NameToAtom; + + std::unique_ptr<MemoryBuffer> _mb; + MachOLinkingContext *_ctx; + SectionToAtoms _sectionAtoms; + NameToAtom _undefAtoms; +}; + +class MachODylibFile : public SharedLibraryFile { +public: + MachODylibFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx) + : SharedLibraryFile(mb->getBufferIdentifier()), + _mb(std::move(mb)), _ctx(ctx) {} + + MachODylibFile(StringRef path) : SharedLibraryFile(path) {} + + const SharedLibraryAtom *exports(StringRef name, bool isData) const override { + // Pass down _installName so that if this requested symbol + // is re-exported through this dylib, the SharedLibraryAtom's loadName() + // is this dylib installName and not the implementation dylib's. + // NOTE: isData is not needed for dylibs (it matters for static libs). + return exports(name, _installName); + } + + /// Adds symbol name that this dylib exports. The corresponding + /// SharedLibraryAtom is created lazily (since most symbols are not used). + void addExportedSymbol(StringRef name, bool weakDef, bool copyRefs) { + if (copyRefs) { + name = name.copy(allocator()); + } + AtomAndFlags info(weakDef); + _nameToAtom[name] = info; + } + + void addReExportedDylib(StringRef dylibPath) { + _reExportedDylibs.emplace_back(dylibPath); + } + + StringRef installName() { return _installName; } + uint32_t currentVersion() { return _currentVersion; } + uint32_t compatVersion() { return _compatVersion; } + + void setInstallName(StringRef name) { _installName = name; } + void setCompatVersion(uint32_t version) { _compatVersion = version; } + void setCurrentVersion(uint32_t version) { _currentVersion = version; } + + typedef std::function<MachODylibFile *(StringRef)> FindDylib; + + void loadReExportedDylibs(FindDylib find) { + for (ReExportedDylib &entry : _reExportedDylibs) { + entry.file = find(entry.path); + } + } + + StringRef getDSOName() const override { return _installName; } + + std::error_code doParse() override { + // Convert binary file to normalized mach-o. + auto normFile = normalized::readBinary(_mb, _ctx->arch()); + if (std::error_code ec = normFile.getError()) + return ec; + // Convert normalized mach-o to atoms. + if (std::error_code ec = normalized::normalizedDylibToAtoms( + this, **normFile, false)) + return ec; + return std::error_code(); + } + +private: + const SharedLibraryAtom *exports(StringRef name, + StringRef installName) const { + // First, check if requested symbol is directly implemented by this dylib. + auto entry = _nameToAtom.find(name); + if (entry != _nameToAtom.end()) { + if (!entry->second.atom) { + // Lazily create SharedLibraryAtom. + entry->second.atom = + new (allocator()) MachOSharedLibraryAtom(*this, name, installName, + entry->second.weakDef); + } + return entry->second.atom; + } + + // Next, check if symbol is implemented in some re-exported dylib. + for (const ReExportedDylib &dylib : _reExportedDylibs) { + assert(dylib.file); + auto atom = dylib.file->exports(name, installName); + if (atom) + return atom; + } + + // Symbol not exported or re-exported by this dylib. + return nullptr; + } + + + struct ReExportedDylib { + ReExportedDylib(StringRef p) : path(p), file(nullptr) { } + StringRef path; + MachODylibFile *file; + }; + + struct AtomAndFlags { + AtomAndFlags() : atom(nullptr), weakDef(false) { } + AtomAndFlags(bool weak) : atom(nullptr), weakDef(weak) { } + const SharedLibraryAtom *atom; + bool weakDef; + }; + + std::unique_ptr<MemoryBuffer> _mb; + MachOLinkingContext *_ctx; + StringRef _installName; + uint32_t _currentVersion; + uint32_t _compatVersion; + std::vector<ReExportedDylib> _reExportedDylibs; + mutable std::unordered_map<StringRef, AtomAndFlags> _nameToAtom; +}; + +} // end namespace mach_o +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/MachO/GOTPass.cpp b/lib/ReaderWriter/MachO/GOTPass.cpp new file mode 100644 index 0000000000000..1ddec4003cbd8 --- /dev/null +++ b/lib/ReaderWriter/MachO/GOTPass.cpp @@ -0,0 +1,185 @@ +//===- lib/ReaderWriter/MachO/GOTPass.cpp ---------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This linker pass transforms all GOT kind references to real references. +/// That is, in assembly you can write something like: +/// movq foo@GOTPCREL(%rip), %rax +/// which means you want to load a pointer to "foo" out of the GOT (global +/// Offsets Table). In the object file, the Atom containing this instruction +/// has a Reference whose target is an Atom named "foo" and the Reference +/// kind is a GOT load. The linker needs to instantiate a pointer sized +/// GOT entry. This is done be creating a GOT Atom to represent that pointer +/// sized data in this pass, and altering the Atom graph so the Reference now +/// points to the GOT Atom entry (corresponding to "foo") and changing the +/// Reference Kind to reflect it is now pointing to a GOT entry (rather +/// then needing a GOT entry). +/// +/// There is one optimization the linker can do here. If the target of the GOT +/// is in the same linkage unit and does not need to be interposable, and +/// the GOT use is just a load (not some other operation), this pass can +/// transform that load into an LEA (add). This optimizes away one memory load +/// which at runtime that could stall the pipeline. This optimization only +/// works for architectures in which a (GOT) load instruction can be change to +/// an LEA instruction that is the same size. The method isGOTAccess() should +/// only return true for "canBypassGOT" if this optimization is supported. +/// +//===----------------------------------------------------------------------===// + +#include "ArchHandler.h" +#include "File.h" +#include "MachOPasses.h" +#include "lld/Core/DefinedAtom.h" +#include "lld/Core/File.h" +#include "lld/Core/LLVM.h" +#include "lld/Core/Reference.h" +#include "lld/Core/Simple.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" + +namespace lld { +namespace mach_o { + + +// +// GOT Entry Atom created by the GOT pass. +// +class GOTEntryAtom : public SimpleDefinedAtom { +public: + GOTEntryAtom(const File &file, bool is64, StringRef name) + : SimpleDefinedAtom(file), _is64(is64), _name(name) { } + + ContentType contentType() const override { + return DefinedAtom::typeGOT; + } + + Alignment alignment() const override { + return Alignment(_is64 ? 3 : 2); + } + + uint64_t size() const override { + return _is64 ? 8 : 4; + } + + ContentPermissions permissions() const override { + return DefinedAtom::permRW_; + } + + ArrayRef<uint8_t> rawContent() const override { + static const uint8_t zeros[] = + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + return llvm::makeArrayRef(zeros, size()); + } + + StringRef slotName() const { + return _name; + } + +private: + const bool _is64; + StringRef _name; +}; + + +/// Pass for instantiating and optimizing GOT slots. +/// +class GOTPass : public Pass { +public: + GOTPass(const MachOLinkingContext &context) + : _context(context), _archHandler(_context.archHandler()), + _file("<mach-o GOT Pass>") { } + +private: + + void perform(std::unique_ptr<MutableFile> &mergedFile) override { + // Scan all references in all atoms. + for (const DefinedAtom *atom : mergedFile->defined()) { + for (const Reference *ref : *atom) { + // Look at instructions accessing the GOT. + bool canBypassGOT; + if (!_archHandler.isGOTAccess(*ref, canBypassGOT)) + continue; + const Atom *target = ref->target(); + assert(target != nullptr); + + if (!shouldReplaceTargetWithGOTAtom(target, canBypassGOT)) { + // Update reference kind to reflect that target is a direct accesss. + _archHandler.updateReferenceToGOT(ref, false); + } else { + // Replace the target with a reference to a GOT entry. + const DefinedAtom *gotEntry = makeGOTEntry(target); + const_cast<Reference *>(ref)->setTarget(gotEntry); + // Update reference kind to reflect that target is now a GOT entry. + _archHandler.updateReferenceToGOT(ref, true); + } + } + } + + // Sort and add all created GOT Atoms to master file + std::vector<const GOTEntryAtom *> entries; + entries.reserve(_targetToGOT.size()); + for (auto &it : _targetToGOT) + entries.push_back(it.second); + std::sort(entries.begin(), entries.end(), + [](const GOTEntryAtom *left, const GOTEntryAtom *right) { + return (left->slotName().compare(right->slotName()) < 0); + }); + for (const GOTEntryAtom *slot : entries) + mergedFile->addAtom(*slot); + } + + bool shouldReplaceTargetWithGOTAtom(const Atom *target, bool canBypassGOT) { + // Accesses to shared library symbols must go through GOT. + if (isa<SharedLibraryAtom>(target)) + return true; + // Accesses to interposable symbols in same linkage unit must also go + // through GOT. + const DefinedAtom *defTarget = dyn_cast<DefinedAtom>(target); + if (defTarget != nullptr && + defTarget->interposable() != DefinedAtom::interposeNo) { + assert(defTarget->scope() != DefinedAtom::scopeTranslationUnit); + return true; + } + // Target does not require indirection. So, if instruction allows GOT to be + // by-passed, do that optimization and don't create GOT entry. + return !canBypassGOT; + } + + const DefinedAtom *makeGOTEntry(const Atom *target) { + auto pos = _targetToGOT.find(target); + if (pos == _targetToGOT.end()) { + GOTEntryAtom *gotEntry = new (_file.allocator()) + GOTEntryAtom(_file, _context.is64Bit(), target->name()); + _targetToGOT[target] = gotEntry; + const ArchHandler::ReferenceInfo &nlInfo = _archHandler.stubInfo(). + nonLazyPointerReferenceToBinder; + gotEntry->addReference(Reference::KindNamespace::mach_o, nlInfo.arch, + nlInfo.kind, 0, target, 0); + return gotEntry; + } + return pos->second; + } + + + const MachOLinkingContext &_context; + mach_o::ArchHandler &_archHandler; + MachOFile _file; + llvm::DenseMap<const Atom*, const GOTEntryAtom*> _targetToGOT; +}; + + + +void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx) { + assert(ctx.needsGOTPass()); + pm.add(llvm::make_unique<GOTPass>(ctx)); +} + + +} // end namesapce mach_o +} // end namesapce lld diff --git a/lib/ReaderWriter/MachO/LayoutPass.cpp b/lib/ReaderWriter/MachO/LayoutPass.cpp new file mode 100644 index 0000000000000..2d096e4c1a6a0 --- /dev/null +++ b/lib/ReaderWriter/MachO/LayoutPass.cpp @@ -0,0 +1,482 @@ +//===-- ReaderWriter/MachO/LayoutPass.cpp - Layout atoms ------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LayoutPass.h" +#include "lld/Core/Instrumentation.h" +#include "lld/Core/Parallel.h" +#include "lld/Core/PassManager.h" +#include "lld/ReaderWriter/MachOLinkingContext.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Debug.h" +#include <algorithm> +#include <set> + +using namespace lld; + +#define DEBUG_TYPE "LayoutPass" + +namespace lld { +namespace mach_o { + +static bool compareAtoms(const LayoutPass::SortKey &, + const LayoutPass::SortKey &, + LayoutPass::SortOverride customSorter); + +#ifndef NDEBUG +// Return "reason (leftval, rightval)" +static std::string formatReason(StringRef reason, int leftVal, int rightVal) { + return (Twine(reason) + " (" + Twine(leftVal) + ", " + Twine(rightVal) + ")") + .str(); +} + +// Less-than relationship of two atoms must be transitive, which is, if a < b +// and b < c, a < c must be true. This function checks the transitivity by +// checking the sort results. +static void checkTransitivity(std::vector<LayoutPass::SortKey> &vec, + LayoutPass::SortOverride customSorter) { + for (auto i = vec.begin(), e = vec.end(); (i + 1) != e; ++i) { + for (auto j = i + 1; j != e; ++j) { + assert(compareAtoms(*i, *j, customSorter)); + assert(!compareAtoms(*j, *i, customSorter)); + } + } +} + +// Helper functions to check follow-on graph. +typedef llvm::DenseMap<const DefinedAtom *, const DefinedAtom *> AtomToAtomT; + +static std::string atomToDebugString(const Atom *atom) { + const DefinedAtom *definedAtom = dyn_cast<DefinedAtom>(atom); + std::string str; + llvm::raw_string_ostream s(str); + if (definedAtom->name().empty()) + s << "<anonymous " << definedAtom << ">"; + else + s << definedAtom->name(); + s << " in "; + if (definedAtom->customSectionName().empty()) + s << "<anonymous>"; + else + s << definedAtom->customSectionName(); + s.flush(); + return str; +} + +static void showCycleDetectedError(const Registry ®istry, + AtomToAtomT &followOnNexts, + const DefinedAtom *atom) { + const DefinedAtom *start = atom; + llvm::dbgs() << "There's a cycle in a follow-on chain!\n"; + do { + llvm::dbgs() << " " << atomToDebugString(atom) << "\n"; + for (const Reference *ref : *atom) { + StringRef kindValStr; + if (!registry.referenceKindToString(ref->kindNamespace(), ref->kindArch(), + ref->kindValue(), kindValStr)) { + kindValStr = "<unknown>"; + } + llvm::dbgs() << " " << kindValStr + << ": " << atomToDebugString(ref->target()) << "\n"; + } + atom = followOnNexts[atom]; + } while (atom != start); + llvm::report_fatal_error("Cycle detected"); +} + +/// Exit if there's a cycle in a followon chain reachable from the +/// given root atom. Uses the tortoise and hare algorithm to detect a +/// cycle. +static void checkNoCycleInFollowonChain(const Registry ®istry, + AtomToAtomT &followOnNexts, + const DefinedAtom *root) { + const DefinedAtom *tortoise = root; + const DefinedAtom *hare = followOnNexts[root]; + while (true) { + if (!tortoise || !hare) + return; + if (tortoise == hare) + showCycleDetectedError(registry, followOnNexts, tortoise); + tortoise = followOnNexts[tortoise]; + hare = followOnNexts[followOnNexts[hare]]; + } +} + +static void checkReachabilityFromRoot(AtomToAtomT &followOnRoots, + const DefinedAtom *atom) { + if (!atom) return; + auto i = followOnRoots.find(atom); + if (i == followOnRoots.end()) { + llvm_unreachable(((Twine("Atom <") + atomToDebugString(atom) + + "> has no follow-on root!")) + .str() + .c_str()); + } + const DefinedAtom *ap = i->second; + while (true) { + const DefinedAtom *next = followOnRoots[ap]; + if (!next) { + llvm_unreachable((Twine("Atom <" + atomToDebugString(atom) + + "> is not reachable from its root!")) + .str() + .c_str()); + } + if (next == ap) + return; + ap = next; + } +} + +static void printDefinedAtoms(const MutableFile::DefinedAtomRange &atomRange) { + for (const DefinedAtom *atom : atomRange) { + llvm::dbgs() << " file=" << atom->file().path() + << ", name=" << atom->name() + << ", size=" << atom->size() + << ", type=" << atom->contentType() + << ", ordinal=" << atom->ordinal() + << "\n"; + } +} + +/// Verify that the followon chain is sane. Should not be called in +/// release binary. +void LayoutPass::checkFollowonChain(MutableFile::DefinedAtomRange &range) { + ScopedTask task(getDefaultDomain(), "LayoutPass::checkFollowonChain"); + + // Verify that there's no cycle in follow-on chain. + std::set<const DefinedAtom *> roots; + for (const auto &ai : _followOnRoots) + roots.insert(ai.second); + for (const DefinedAtom *root : roots) + checkNoCycleInFollowonChain(_registry, _followOnNexts, root); + + // Verify that all the atoms in followOnNexts have references to + // their roots. + for (const auto &ai : _followOnNexts) { + checkReachabilityFromRoot(_followOnRoots, ai.first); + checkReachabilityFromRoot(_followOnRoots, ai.second); + } +} +#endif // #ifndef NDEBUG + +/// The function compares atoms by sorting atoms in the following order +/// a) Sorts atoms by their ordinal overrides (layout-after/ingroup) +/// b) Sorts atoms by their permissions +/// c) Sorts atoms by their content +/// d) Sorts atoms by custom sorter +/// e) Sorts atoms on how they appear using File Ordinality +/// f) Sorts atoms on how they appear within the File +static bool compareAtomsSub(const LayoutPass::SortKey &lc, + const LayoutPass::SortKey &rc, + LayoutPass::SortOverride customSorter, + std::string &reason) { + const DefinedAtom *left = lc._atom; + const DefinedAtom *right = rc._atom; + if (left == right) { + reason = "same"; + return false; + } + + // Find the root of the chain if it is a part of a follow-on chain. + const DefinedAtom *leftRoot = lc._root; + const DefinedAtom *rightRoot = rc._root; + + // Sort atoms by their ordinal overrides only if they fall in the same + // chain. + if (leftRoot == rightRoot) { + DEBUG(reason = formatReason("override", lc._override, rc._override)); + return lc._override < rc._override; + } + + // Sort same permissions together. + DefinedAtom::ContentPermissions leftPerms = leftRoot->permissions(); + DefinedAtom::ContentPermissions rightPerms = rightRoot->permissions(); + + if (leftPerms != rightPerms) { + DEBUG(reason = + formatReason("contentPerms", (int)leftPerms, (int)rightPerms)); + return leftPerms < rightPerms; + } + + // Sort same content types together. + DefinedAtom::ContentType leftType = leftRoot->contentType(); + DefinedAtom::ContentType rightType = rightRoot->contentType(); + + if (leftType != rightType) { + DEBUG(reason = formatReason("contentType", (int)leftType, (int)rightType)); + return leftType < rightType; + } + + // Use custom sorter if supplied. + if (customSorter) { + bool leftBeforeRight; + if (customSorter(leftRoot, rightRoot, leftBeforeRight)) + return leftBeforeRight; + } + + // Sort by .o order. + const File *leftFile = &leftRoot->file(); + const File *rightFile = &rightRoot->file(); + + if (leftFile != rightFile) { + DEBUG(reason = formatReason(".o order", (int)leftFile->ordinal(), + (int)rightFile->ordinal())); + return leftFile->ordinal() < rightFile->ordinal(); + } + + // Sort by atom order with .o file. + uint64_t leftOrdinal = leftRoot->ordinal(); + uint64_t rightOrdinal = rightRoot->ordinal(); + + if (leftOrdinal != rightOrdinal) { + DEBUG(reason = formatReason("ordinal", (int)leftRoot->ordinal(), + (int)rightRoot->ordinal())); + return leftOrdinal < rightOrdinal; + } + + llvm::errs() << "Unordered: <" << left->name() << "> <" + << right->name() << ">\n"; + llvm_unreachable("Atoms with Same Ordinal!"); +} + +static bool compareAtoms(const LayoutPass::SortKey &lc, + const LayoutPass::SortKey &rc, + LayoutPass::SortOverride customSorter) { + std::string reason; + bool result = compareAtomsSub(lc, rc, customSorter, reason); + DEBUG({ + StringRef comp = result ? "<" : ">="; + llvm::dbgs() << "Layout: '" << lc._atom->name() << "' " << comp << " '" + << rc._atom->name() << "' (" << reason << ")\n"; + }); + return result; +} + +LayoutPass::LayoutPass(const Registry ®istry, SortOverride sorter) + : _registry(registry), _customSorter(sorter) {} + +// Returns the atom immediately followed by the given atom in the followon +// chain. +const DefinedAtom *LayoutPass::findAtomFollowedBy( + const DefinedAtom *targetAtom) { + // Start from the beginning of the chain and follow the chain until + // we find the targetChain. + const DefinedAtom *atom = _followOnRoots[targetAtom]; + while (true) { + const DefinedAtom *prevAtom = atom; + AtomToAtomT::iterator targetFollowOnAtomsIter = _followOnNexts.find(atom); + // The target atom must be in the chain of its root. + assert(targetFollowOnAtomsIter != _followOnNexts.end()); + atom = targetFollowOnAtomsIter->second; + if (atom == targetAtom) + return prevAtom; + } +} + +// Check if all the atoms followed by the given target atom are of size zero. +// When this method is called, an atom being added is not of size zero and +// will be added to the head of the followon chain. All the atoms between the +// atom and the targetAtom (specified by layout-after) need to be of size zero +// in this case. Otherwise the desired layout is impossible. +bool LayoutPass::checkAllPrevAtomsZeroSize(const DefinedAtom *targetAtom) { + const DefinedAtom *atom = _followOnRoots[targetAtom]; + while (true) { + if (atom == targetAtom) + return true; + if (atom->size() != 0) + // TODO: print warning that an impossible layout is being desired by the + // user. + return false; + AtomToAtomT::iterator targetFollowOnAtomsIter = _followOnNexts.find(atom); + // The target atom must be in the chain of its root. + assert(targetFollowOnAtomsIter != _followOnNexts.end()); + atom = targetFollowOnAtomsIter->second; + } +} + +// Set the root of all atoms in targetAtom's chain to the given root. +void LayoutPass::setChainRoot(const DefinedAtom *targetAtom, + const DefinedAtom *root) { + // Walk through the followon chain and override each node's root. + while (true) { + _followOnRoots[targetAtom] = root; + AtomToAtomT::iterator targetFollowOnAtomsIter = + _followOnNexts.find(targetAtom); + if (targetFollowOnAtomsIter == _followOnNexts.end()) + return; + targetAtom = targetFollowOnAtomsIter->second; + } +} + +/// This pass builds the followon tables described by two DenseMaps +/// followOnRoots and followonNexts. +/// The followOnRoots map contains a mapping of a DefinedAtom to its root +/// The followOnNexts map contains a mapping of what DefinedAtom follows the +/// current Atom +/// The algorithm follows a very simple approach +/// a) If the atom is first seen, then make that as the root atom +/// b) The targetAtom which this Atom contains, has the root thats set to the +/// root of the current atom +/// c) If the targetAtom is part of a different tree and the root of the +/// targetAtom is itself, Chain all the atoms that are contained in the tree +/// to the current Tree +/// d) If the targetAtom is part of a different chain and the root of the +/// targetAtom until the targetAtom has all atoms of size 0, then chain the +/// targetAtoms and its tree to the current chain +void LayoutPass::buildFollowOnTable(MutableFile::DefinedAtomRange &range) { + ScopedTask task(getDefaultDomain(), "LayoutPass::buildFollowOnTable"); + // Set the initial size of the followon and the followonNext hash to the + // number of atoms that we have. + _followOnRoots.resize(range.size()); + _followOnNexts.resize(range.size()); + for (const DefinedAtom *ai : range) { + for (const Reference *r : *ai) { + if (r->kindNamespace() != lld::Reference::KindNamespace::all || + r->kindValue() != lld::Reference::kindLayoutAfter) + continue; + const DefinedAtom *targetAtom = dyn_cast<DefinedAtom>(r->target()); + _followOnNexts[ai] = targetAtom; + + // If we find a followon for the first time, let's make that atom as the + // root atom. + if (_followOnRoots.count(ai) == 0) + _followOnRoots[ai] = ai; + + auto iter = _followOnRoots.find(targetAtom); + if (iter == _followOnRoots.end()) { + // If the targetAtom is not a root of any chain, let's make the root of + // the targetAtom to the root of the current chain. + + // The expression m[i] = m[j] where m is a DenseMap and i != j is not + // safe. m[j] returns a reference, which would be invalidated when a + // rehashing occurs. If rehashing occurs to make room for m[i], m[j] + // becomes invalid, and that invalid reference would be used as the RHS + // value of the expression. + // Copy the value to workaround. + const DefinedAtom *tmp = _followOnRoots[ai]; + _followOnRoots[targetAtom] = tmp; + continue; + } + if (iter->second == targetAtom) { + // If the targetAtom is the root of a chain, the chain becomes part of + // the current chain. Rewrite the subchain's root to the current + // chain's root. + setChainRoot(targetAtom, _followOnRoots[ai]); + continue; + } + // The targetAtom is already a part of a chain. If the current atom is + // of size zero, we can insert it in the middle of the chain just + // before the target atom, while not breaking other atom's followon + // relationships. If it's not, we can only insert the current atom at + // the beginning of the chain. All the atoms followed by the target + // atom must be of size zero in that case to satisfy the followon + // relationships. + size_t currentAtomSize = ai->size(); + if (currentAtomSize == 0) { + const DefinedAtom *targetPrevAtom = findAtomFollowedBy(targetAtom); + _followOnNexts[targetPrevAtom] = ai; + const DefinedAtom *tmp = _followOnRoots[targetPrevAtom]; + _followOnRoots[ai] = tmp; + continue; + } + if (!checkAllPrevAtomsZeroSize(targetAtom)) + break; + _followOnNexts[ai] = _followOnRoots[targetAtom]; + setChainRoot(_followOnRoots[targetAtom], _followOnRoots[ai]); + } + } +} + +/// Build an ordinal override map by traversing the followon chain, and +/// assigning ordinals to each atom, if the atoms have their ordinals +/// already assigned skip the atom and move to the next. This is the +/// main map thats used to sort the atoms while comparing two atoms together +void LayoutPass::buildOrdinalOverrideMap(MutableFile::DefinedAtomRange &range) { + ScopedTask task(getDefaultDomain(), "LayoutPass::buildOrdinalOverrideMap"); + uint64_t index = 0; + for (const DefinedAtom *ai : range) { + const DefinedAtom *atom = ai; + if (_ordinalOverrideMap.find(atom) != _ordinalOverrideMap.end()) + continue; + AtomToAtomT::iterator start = _followOnRoots.find(atom); + if (start == _followOnRoots.end()) + continue; + for (const DefinedAtom *nextAtom = start->second; nextAtom != NULL; + nextAtom = _followOnNexts[nextAtom]) { + AtomToOrdinalT::iterator pos = _ordinalOverrideMap.find(nextAtom); + if (pos == _ordinalOverrideMap.end()) + _ordinalOverrideMap[nextAtom] = index++; + } + } +} + +std::vector<LayoutPass::SortKey> +LayoutPass::decorate(MutableFile::DefinedAtomRange &atomRange) const { + std::vector<SortKey> ret; + for (const DefinedAtom *atom : atomRange) { + auto ri = _followOnRoots.find(atom); + auto oi = _ordinalOverrideMap.find(atom); + const DefinedAtom *root = (ri == _followOnRoots.end()) ? atom : ri->second; + uint64_t override = (oi == _ordinalOverrideMap.end()) ? 0 : oi->second; + ret.push_back(SortKey(atom, root, override)); + } + return ret; +} + +void LayoutPass::undecorate(MutableFile::DefinedAtomRange &atomRange, + std::vector<SortKey> &keys) const { + size_t i = 0; + for (SortKey &k : keys) + atomRange[i++] = k._atom; +} + +/// Perform the actual pass +void LayoutPass::perform(std::unique_ptr<MutableFile> &mergedFile) { + // sort the atoms + ScopedTask task(getDefaultDomain(), "LayoutPass"); + MutableFile::DefinedAtomRange atomRange = mergedFile->definedAtoms(); + + // Build follow on tables + buildFollowOnTable(atomRange); + + // Check the structure of followon graph if running in debug mode. + DEBUG(checkFollowonChain(atomRange)); + + // Build override maps + buildOrdinalOverrideMap(atomRange); + + DEBUG({ + llvm::dbgs() << "unsorted atoms:\n"; + printDefinedAtoms(atomRange); + }); + + std::vector<LayoutPass::SortKey> vec = decorate(atomRange); + parallel_sort(vec.begin(), vec.end(), + [&](const LayoutPass::SortKey &l, const LayoutPass::SortKey &r) -> bool { + return compareAtoms(l, r, _customSorter); + }); + DEBUG(checkTransitivity(vec, _customSorter)); + undecorate(atomRange, vec); + + DEBUG({ + llvm::dbgs() << "sorted atoms:\n"; + printDefinedAtoms(atomRange); + }); +} + +void addLayoutPass(PassManager &pm, const MachOLinkingContext &ctx) { + pm.add(llvm::make_unique<LayoutPass>( + ctx.registry(), [&](const DefinedAtom * left, const DefinedAtom * right, + bool & leftBeforeRight) ->bool { + return ctx.customAtomOrderer(left, right, leftBeforeRight); + })); +} + +} // namespace mach_o +} // namespace lld diff --git a/lib/ReaderWriter/MachO/LayoutPass.h b/lib/ReaderWriter/MachO/LayoutPass.h new file mode 100644 index 0000000000000..186f29be0719c --- /dev/null +++ b/lib/ReaderWriter/MachO/LayoutPass.h @@ -0,0 +1,97 @@ +//===------ lib/ReaderWriter/MachO/LayoutPass.h - Handles Layout of atoms -===// +// +// 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_LAYOUT_PASS_H +#define LLD_READER_WRITER_MACHO_LAYOUT_PASS_H + +#include "lld/Core/File.h" +#include "lld/Core/Pass.h" +#include "lld/Core/Reader.h" +#include "llvm/ADT/DenseMap.h" +#include <map> +#include <string> +#include <vector> + +namespace lld { +class DefinedAtom; +class MutableFile; + +namespace mach_o { + +/// This linker pass does the layout of the atoms. The pass is done after the +/// order their .o files were found on the command line, then by order of the +/// atoms (address) in the .o file. But some atoms have a preferred location +/// in their section (such as pinned to the start or end of the section), so +/// the sort must take that into account too. +class LayoutPass : public Pass { +public: + struct SortKey { + SortKey(const DefinedAtom *atom, const DefinedAtom *root, uint64_t override) + : _atom(atom), _root(root), _override(override) {} + const DefinedAtom *_atom; + const DefinedAtom *_root; + uint64_t _override; + }; + + typedef std::function<bool (const DefinedAtom *left, const DefinedAtom *right, + bool &leftBeforeRight)> SortOverride; + + LayoutPass(const Registry ®istry, SortOverride sorter); + + /// Sorts atoms in mergedFile by content type then by command line order. + void perform(std::unique_ptr<MutableFile> &mergedFile) override; + + virtual ~LayoutPass() {} + +private: + // Build the followOn atoms chain as specified by the kindLayoutAfter + // reference type + void buildFollowOnTable(MutableFile::DefinedAtomRange &range); + + // Build a map of Atoms to ordinals for sorting the atoms + void buildOrdinalOverrideMap(MutableFile::DefinedAtomRange &range); + + const Registry &_registry; + SortOverride _customSorter; + + typedef llvm::DenseMap<const DefinedAtom *, const DefinedAtom *> AtomToAtomT; + typedef llvm::DenseMap<const DefinedAtom *, uint64_t> AtomToOrdinalT; + + // A map to be used to sort atoms. It represents the order of atoms in the + // result; if Atom X is mapped to atom Y in this map, X will be located + // immediately before Y in the output file. Y might be mapped to another + // atom, constructing a follow-on chain. An atom cannot be mapped to more + // than one atom unless all but one atom are of size zero. + AtomToAtomT _followOnNexts; + + // A map to be used to sort atoms. It's a map from an atom to its root of + // follow-on chain. A root atom is mapped to itself. If an atom is not in + // _followOnNexts, the atom is not in this map, and vice versa. + AtomToAtomT _followOnRoots; + + AtomToOrdinalT _ordinalOverrideMap; + + // Helper methods for buildFollowOnTable(). + const DefinedAtom *findAtomFollowedBy(const DefinedAtom *targetAtom); + bool checkAllPrevAtomsZeroSize(const DefinedAtom *targetAtom); + + void setChainRoot(const DefinedAtom *targetAtom, const DefinedAtom *root); + + std::vector<SortKey> decorate(MutableFile::DefinedAtomRange &atomRange) const; + void undecorate(MutableFile::DefinedAtomRange &atomRange, + std::vector<SortKey> &keys) const; + + // Check if the follow-on graph is a correct structure. For debugging only. + void checkFollowonChain(MutableFile::DefinedAtomRange &range); +}; + +} // namespace mach_o +} // namespace lld + +#endif // LLD_READER_WRITER_MACHO_LAYOUT_PASS_H diff --git a/lib/ReaderWriter/MachO/MachOLinkingContext.cpp b/lib/ReaderWriter/MachO/MachOLinkingContext.cpp new file mode 100644 index 0000000000000..92385cf3e820e --- /dev/null +++ b/lib/ReaderWriter/MachO/MachOLinkingContext.cpp @@ -0,0 +1,969 @@ +//===- lib/ReaderWriter/MachO/MachOLinkingContext.cpp ---------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lld/ReaderWriter/MachOLinkingContext.h" +#include "ArchHandler.h" +#include "File.h" +#include "MachONormalizedFile.h" +#include "MachOPasses.h" +#include "lld/Core/ArchiveLibraryFile.h" +#include "lld/Core/PassManager.h" +#include "lld/Core/Reader.h" +#include "lld/Core/Writer.h" +#include "lld/Driver/Driver.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Config/config.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/MachO.h" +#include "llvm/Support/Path.h" +#include <algorithm> + +#if defined(HAVE_CXXABI_H) +#include <cxxabi.h> +#endif + +using lld::mach_o::ArchHandler; +using lld::mach_o::MachODylibFile; +using namespace llvm::MachO; + +namespace lld { + +bool MachOLinkingContext::parsePackedVersion(StringRef str, uint32_t &result) { + result = 0; + + if (str.empty()) + return false; + + SmallVector<StringRef, 3> parts; + llvm::SplitString(str, parts, "."); + + unsigned long long num; + if (llvm::getAsUnsignedInteger(parts[0], 10, num)) + return true; + if (num > 65535) + return true; + result = num << 16; + + if (parts.size() > 1) { + if (llvm::getAsUnsignedInteger(parts[1], 10, num)) + return true; + if (num > 255) + return true; + result |= (num << 8); + } + + if (parts.size() > 2) { + if (llvm::getAsUnsignedInteger(parts[2], 10, num)) + return true; + if (num > 255) + return true; + result |= num; + } + + return false; +} + + +MachOLinkingContext::ArchInfo MachOLinkingContext::_s_archInfos[] = { + { "x86_64", arch_x86_64, true, CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL }, + { "i386", arch_x86, true, CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL }, + { "ppc", arch_ppc, false, CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL }, + { "armv6", arch_armv6, true, CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6 }, + { "armv7", arch_armv7, true, CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7 }, + { "armv7s", arch_armv7s, true, CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S }, + { "arm64", arch_arm64, true, CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL }, + { "", arch_unknown,false, 0, 0 } +}; + +MachOLinkingContext::Arch +MachOLinkingContext::archFromCpuType(uint32_t cputype, uint32_t cpusubtype) { + for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) { + if ((info->cputype == cputype) && (info->cpusubtype == cpusubtype)) + return info->arch; + } + return arch_unknown; +} + +MachOLinkingContext::Arch +MachOLinkingContext::archFromName(StringRef archName) { + for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) { + if (info->archName.equals(archName)) + return info->arch; + } + return arch_unknown; +} + +StringRef MachOLinkingContext::nameFromArch(Arch arch) { + for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) { + if (info->arch == arch) + return info->archName; + } + return "<unknown>"; +} + +uint32_t MachOLinkingContext::cpuTypeFromArch(Arch arch) { + assert(arch != arch_unknown); + for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) { + if (info->arch == arch) + return info->cputype; + } + llvm_unreachable("Unknown arch type"); +} + +uint32_t MachOLinkingContext::cpuSubtypeFromArch(Arch arch) { + assert(arch != arch_unknown); + for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) { + if (info->arch == arch) + return info->cpusubtype; + } + llvm_unreachable("Unknown arch type"); +} + +bool MachOLinkingContext::isThinObjectFile(StringRef path, Arch &arch) { + return mach_o::normalized::isThinObjectFile(path, arch); +} + +bool MachOLinkingContext::sliceFromFatFile(const MemoryBuffer &mb, + uint32_t &offset, + uint32_t &size) { + return mach_o::normalized::sliceFromFatFile(mb, _arch, offset, size); +} + +MachOLinkingContext::MachOLinkingContext() + : _outputMachOType(MH_EXECUTE), _outputMachOTypeStatic(false), + _doNothing(false), _pie(false), _arch(arch_unknown), _os(OS::macOSX), + _osMinVersion(0), _pageZeroSize(0), _pageSize(4096), _baseAddress(0), + _compatibilityVersion(0), _currentVersion(0), _deadStrippableDylib(false), + _printAtoms(false), _testingFileUsage(false), _keepPrivateExterns(false), + _demangle(false), _archHandler(nullptr), + _exportMode(ExportMode::globals), + _debugInfoMode(DebugInfoMode::addDebugMap), _orderFileEntries(0) {} + +MachOLinkingContext::~MachOLinkingContext() {} + +void MachOLinkingContext::configure(HeaderFileType type, Arch arch, OS os, + uint32_t minOSVersion) { + _outputMachOType = type; + _arch = arch; + _os = os; + _osMinVersion = minOSVersion; + + // If min OS not specified on command line, use reasonable defaults. + if (minOSVersion == 0) { + switch (_arch) { + case arch_x86_64: + case arch_x86: + parsePackedVersion("10.8", _osMinVersion); + _os = MachOLinkingContext::OS::macOSX; + break; + case arch_armv6: + case arch_armv7: + case arch_armv7s: + case arch_arm64: + parsePackedVersion("7.0", _osMinVersion); + _os = MachOLinkingContext::OS::iOS; + break; + default: + break; + } + } + + switch (_outputMachOType) { + case llvm::MachO::MH_EXECUTE: + // If targeting newer OS, use _main + if (minOS("10.8", "6.0")) { + _entrySymbolName = "_main"; + } else { + // If targeting older OS, use start (in crt1.o) + _entrySymbolName = "start"; + } + + // __PAGEZERO defaults to 4GB on 64-bit (except for PP64 which lld does not + // support) and 4KB on 32-bit. + if (is64Bit(_arch)) { + _pageZeroSize = 0x100000000; + } else { + _pageZeroSize = 0x1000; + } + + // Make PIE by default when targetting newer OSs. + switch (os) { + case OS::macOSX: + if (minOSVersion >= 0x000A0700) // MacOSX 10.7 + _pie = true; + break; + case OS::iOS: + if (minOSVersion >= 0x00040300) // iOS 4.3 + _pie = true; + break; + case OS::iOS_simulator: + _pie = true; + break; + case OS::unknown: + break; + } + break; + case llvm::MachO::MH_DYLIB: + setGlobalsAreDeadStripRoots(true); + break; + case llvm::MachO::MH_BUNDLE: + break; + case llvm::MachO::MH_OBJECT: + _printRemainingUndefines = false; + _allowRemainingUndefines = true; + default: + break; + } + + // Set default segment page sizes based on arch. + if (arch == arch_arm64) + _pageSize = 4*4096; +} + +uint32_t MachOLinkingContext::getCPUType() const { + return cpuTypeFromArch(_arch); +} + +uint32_t MachOLinkingContext::getCPUSubType() const { + return cpuSubtypeFromArch(_arch); +} + +bool MachOLinkingContext::is64Bit(Arch arch) { + for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) { + if (info->arch == arch) { + return (info->cputype & CPU_ARCH_ABI64); + } + } + // unknown archs are not 64-bit. + return false; +} + +bool MachOLinkingContext::isHostEndian(Arch arch) { + assert(arch != arch_unknown); + for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) { + if (info->arch == arch) { + return (info->littleEndian == llvm::sys::IsLittleEndianHost); + } + } + llvm_unreachable("Unknown arch type"); +} + +bool MachOLinkingContext::isBigEndian(Arch arch) { + assert(arch != arch_unknown); + for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) { + if (info->arch == arch) { + return ! info->littleEndian; + } + } + llvm_unreachable("Unknown arch type"); +} + + + +bool MachOLinkingContext::is64Bit() const { + return is64Bit(_arch); +} + +bool MachOLinkingContext::outputTypeHasEntry() const { + switch (_outputMachOType) { + case MH_EXECUTE: + case MH_DYLINKER: + case MH_PRELOAD: + return true; + default: + return false; + } +} + +bool MachOLinkingContext::needsStubsPass() const { + switch (_outputMachOType) { + case MH_EXECUTE: + return !_outputMachOTypeStatic; + case MH_DYLIB: + case MH_BUNDLE: + return true; + default: + return false; + } +} + +bool MachOLinkingContext::needsGOTPass() const { + // GOT pass not used in -r mode. + if (_outputMachOType == MH_OBJECT) + return false; + // Only some arches use GOT pass. + switch (_arch) { + case arch_x86_64: + case arch_arm64: + return true; + default: + return false; + } +} + +bool MachOLinkingContext::needsCompactUnwindPass() const { + switch (_outputMachOType) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + return archHandler().needsCompactUnwind(); + default: + return false; + } +} + +bool MachOLinkingContext::needsShimPass() const { + // Shim pass only used in final executables. + if (_outputMachOType == MH_OBJECT) + return false; + // Only 32-bit arm arches use Shim pass. + switch (_arch) { + case arch_armv6: + case arch_armv7: + case arch_armv7s: + return true; + default: + return false; + } +} + +StringRef MachOLinkingContext::binderSymbolName() const { + return archHandler().stubInfo().binderSymbolName; +} + + + + +bool MachOLinkingContext::minOS(StringRef mac, StringRef iOS) const { + uint32_t parsedVersion; + switch (_os) { + case OS::macOSX: + if (parsePackedVersion(mac, parsedVersion)) + return false; + return _osMinVersion >= parsedVersion; + case OS::iOS: + case OS::iOS_simulator: + if (parsePackedVersion(iOS, parsedVersion)) + return false; + return _osMinVersion >= parsedVersion; + case OS::unknown: + break; + } + llvm_unreachable("target not configured for iOS or MacOSX"); +} + +bool MachOLinkingContext::addEntryPointLoadCommand() const { + if ((_outputMachOType == MH_EXECUTE) && !_outputMachOTypeStatic) { + return minOS("10.8", "6.0"); + } + return false; +} + +bool MachOLinkingContext::addUnixThreadLoadCommand() const { + switch (_outputMachOType) { + case MH_EXECUTE: + if (_outputMachOTypeStatic) + return true; + else + return !minOS("10.8", "6.0"); + break; + case MH_DYLINKER: + case MH_PRELOAD: + return true; + default: + return false; + } +} + +bool MachOLinkingContext::pathExists(StringRef path) const { + if (!_testingFileUsage) + return llvm::sys::fs::exists(path.str()); + + // Otherwise, we're in test mode: only files explicitly provided on the + // command-line exist. + std::string key = path.str(); + std::replace(key.begin(), key.end(), '\\', '/'); + return _existingPaths.find(key) != _existingPaths.end(); +} + +bool MachOLinkingContext::fileExists(StringRef path) const { + bool found = pathExists(path); + // Log search misses. + if (!found) + addInputFileNotFound(path); + + // When testing, file is never opened, so logging is done here. + if (_testingFileUsage && found) + addInputFileDependency(path); + + return found; +} + +void MachOLinkingContext::setSysLibRoots(const StringRefVector &paths) { + _syslibRoots = paths; +} + +void MachOLinkingContext::addRpath(StringRef rpath) { + _rpaths.push_back(rpath); +} + +void MachOLinkingContext::addModifiedSearchDir(StringRef libPath, + bool isSystemPath) { + bool addedModifiedPath = false; + + // -syslibroot only applies to absolute paths. + if (libPath.startswith("/")) { + for (auto syslibRoot : _syslibRoots) { + SmallString<256> path(syslibRoot); + llvm::sys::path::append(path, libPath); + if (pathExists(path)) { + _searchDirs.push_back(path.str().copy(_allocator)); + addedModifiedPath = true; + } + } + } + + if (addedModifiedPath) + return; + + // Finally, if only one -syslibroot is given, system paths which aren't in it + // get suppressed. + if (_syslibRoots.size() != 1 || !isSystemPath) { + if (pathExists(libPath)) { + _searchDirs.push_back(libPath); + } + } +} + +void MachOLinkingContext::addFrameworkSearchDir(StringRef fwPath, + bool isSystemPath) { + bool pathAdded = false; + + // -syslibroot only used with to absolute framework search paths. + if (fwPath.startswith("/")) { + for (auto syslibRoot : _syslibRoots) { + SmallString<256> path(syslibRoot); + llvm::sys::path::append(path, fwPath); + if (pathExists(path)) { + _frameworkDirs.push_back(path.str().copy(_allocator)); + pathAdded = true; + } + } + } + // If fwPath found in any -syslibroot, then done. + if (pathAdded) + return; + + // If only one -syslibroot, system paths not in that SDK are suppressed. + if (isSystemPath && (_syslibRoots.size() == 1)) + return; + + // Only use raw fwPath if that directory exists. + if (pathExists(fwPath)) + _frameworkDirs.push_back(fwPath); +} + + +ErrorOr<StringRef> +MachOLinkingContext::searchDirForLibrary(StringRef path, + StringRef libName) const { + SmallString<256> fullPath; + if (libName.endswith(".o")) { + // A request ending in .o is special: just search for the file directly. + fullPath.assign(path); + llvm::sys::path::append(fullPath, libName); + if (fileExists(fullPath)) + return fullPath.str().copy(_allocator); + return make_error_code(llvm::errc::no_such_file_or_directory); + } + + // Search for dynamic library + fullPath.assign(path); + llvm::sys::path::append(fullPath, Twine("lib") + libName + ".dylib"); + if (fileExists(fullPath)) + return fullPath.str().copy(_allocator); + + // If not, try for a static library + fullPath.assign(path); + llvm::sys::path::append(fullPath, Twine("lib") + libName + ".a"); + if (fileExists(fullPath)) + return fullPath.str().copy(_allocator); + + return make_error_code(llvm::errc::no_such_file_or_directory); +} + + + +ErrorOr<StringRef> MachOLinkingContext::searchLibrary(StringRef libName) const { + SmallString<256> path; + for (StringRef dir : searchDirs()) { + ErrorOr<StringRef> ec = searchDirForLibrary(dir, libName); + if (ec) + return ec; + } + + return make_error_code(llvm::errc::no_such_file_or_directory); +} + + +ErrorOr<StringRef> MachOLinkingContext::findPathForFramework(StringRef fwName) const{ + SmallString<256> fullPath; + for (StringRef dir : frameworkDirs()) { + fullPath.assign(dir); + llvm::sys::path::append(fullPath, Twine(fwName) + ".framework", fwName); + if (fileExists(fullPath)) + return fullPath.str().copy(_allocator); + } + + return make_error_code(llvm::errc::no_such_file_or_directory); +} + +bool MachOLinkingContext::validateImpl(raw_ostream &diagnostics) { + // TODO: if -arch not specified, look at arch of first .o file. + + if (_currentVersion && _outputMachOType != MH_DYLIB) { + diagnostics << "error: -current_version can only be used with dylibs\n"; + return false; + } + + if (_compatibilityVersion && _outputMachOType != MH_DYLIB) { + diagnostics + << "error: -compatibility_version can only be used with dylibs\n"; + return false; + } + + if (_deadStrippableDylib && _outputMachOType != MH_DYLIB) { + diagnostics + << "error: -mark_dead_strippable_dylib can only be used with dylibs.\n"; + return false; + } + + if (!_bundleLoader.empty() && outputMachOType() != MH_BUNDLE) { + diagnostics + << "error: -bundle_loader can only be used with Mach-O bundles\n"; + return false; + } + + // If -exported_symbols_list used, all exported symbols must be defined. + if (_exportMode == ExportMode::whiteList) { + for (const auto &symbol : _exportedSymbols) + addInitialUndefinedSymbol(symbol.getKey()); + } + + // If -dead_strip, set up initial live symbols. + if (deadStrip()) { + // Entry point is live. + if (outputTypeHasEntry()) + addDeadStripRoot(entrySymbolName()); + // Lazy binding helper is live. + if (needsStubsPass()) + addDeadStripRoot(binderSymbolName()); + // If using -exported_symbols_list, make all exported symbols live. + if (_exportMode == ExportMode::whiteList) { + setGlobalsAreDeadStripRoots(false); + for (const auto &symbol : _exportedSymbols) + addDeadStripRoot(symbol.getKey()); + } + } + + addOutputFileDependency(outputPath()); + + return true; +} + +void MachOLinkingContext::addPasses(PassManager &pm) { + mach_o::addLayoutPass(pm, *this); + if (needsStubsPass()) + mach_o::addStubsPass(pm, *this); + if (needsCompactUnwindPass()) + mach_o::addCompactUnwindPass(pm, *this); + if (needsGOTPass()) + mach_o::addGOTPass(pm, *this); + if (needsShimPass()) + mach_o::addShimPass(pm, *this); // Shim pass must run after stubs pass. +} + +Writer &MachOLinkingContext::writer() const { + if (!_writer) + _writer = createWriterMachO(*this); + return *_writer; +} + +ErrorOr<std::unique_ptr<MemoryBuffer>> +MachOLinkingContext::getMemoryBuffer(StringRef path) { + addInputFileDependency(path); + + ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr = + MemoryBuffer::getFileOrSTDIN(path); + if (std::error_code ec = mbOrErr.getError()) + return ec; + std::unique_ptr<MemoryBuffer> mb = std::move(mbOrErr.get()); + + // If buffer contains a fat file, find required arch in fat buffer + // and switch buffer to point to just that required slice. + uint32_t offset; + uint32_t size; + if (sliceFromFatFile(*mb, offset, size)) + return MemoryBuffer::getFileSlice(path, size, offset); + return std::move(mb); +} + +MachODylibFile* MachOLinkingContext::loadIndirectDylib(StringRef path) { + ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr = getMemoryBuffer(path); + if (mbOrErr.getError()) + return nullptr; + + std::vector<std::unique_ptr<File>> files; + if (registry().loadFile(std::move(mbOrErr.get()), files)) + return nullptr; + assert(files.size() == 1 && "expected one file in dylib"); + files[0]->parse(); + MachODylibFile* result = reinterpret_cast<MachODylibFile*>(files[0].get()); + // Node object now owned by _indirectDylibs vector. + _indirectDylibs.push_back(std::move(files[0])); + return result; +} + + +MachODylibFile* MachOLinkingContext::findIndirectDylib(StringRef path) { + // See if already loaded. + auto pos = _pathToDylibMap.find(path); + if (pos != _pathToDylibMap.end()) + return pos->second; + + // Search -L paths if of the form "libXXX.dylib" + std::pair<StringRef, StringRef> split = path.rsplit('/'); + StringRef leafName = split.second; + if (leafName.startswith("lib") && leafName.endswith(".dylib")) { + // FIXME: Need to enhance searchLibrary() to only look for .dylib + auto libPath = searchLibrary(leafName); + if (!libPath.getError()) { + return loadIndirectDylib(libPath.get()); + } + } + + // Try full path with sysroot. + for (StringRef sysPath : _syslibRoots) { + SmallString<256> fullPath; + fullPath.assign(sysPath); + llvm::sys::path::append(fullPath, path); + if (pathExists(fullPath)) + return loadIndirectDylib(fullPath); + } + + // Try full path. + if (pathExists(path)) { + return loadIndirectDylib(path); + } + + return nullptr; +} + +uint32_t MachOLinkingContext::dylibCurrentVersion(StringRef installName) const { + auto pos = _pathToDylibMap.find(installName); + if (pos != _pathToDylibMap.end()) + return pos->second->currentVersion(); + else + return 0x1000; // 1.0 +} + +uint32_t MachOLinkingContext::dylibCompatVersion(StringRef installName) const { + auto pos = _pathToDylibMap.find(installName); + if (pos != _pathToDylibMap.end()) + return pos->second->compatVersion(); + else + return 0x1000; // 1.0 +} + +bool MachOLinkingContext::createImplicitFiles( + std::vector<std::unique_ptr<File> > &result) { + // Add indirect dylibs by asking each linked dylib to add its indirects. + // Iterate until no more dylibs get loaded. + size_t dylibCount = 0; + while (dylibCount != _allDylibs.size()) { + dylibCount = _allDylibs.size(); + for (MachODylibFile *dylib : _allDylibs) { + dylib->loadReExportedDylibs([this] (StringRef path) -> MachODylibFile* { + return findIndirectDylib(path); }); + } + } + + // Let writer add output type specific extras. + return writer().createImplicitFiles(result); +} + + +void MachOLinkingContext::registerDylib(MachODylibFile *dylib, + bool upward) const { + _allDylibs.insert(dylib); + _pathToDylibMap[dylib->installName()] = dylib; + // If path is different than install name, register path too. + if (!dylib->path().equals(dylib->installName())) + _pathToDylibMap[dylib->path()] = dylib; + if (upward) + _upwardDylibs.insert(dylib); +} + + +bool MachOLinkingContext::isUpwardDylib(StringRef installName) const { + for (MachODylibFile *dylib : _upwardDylibs) { + if (dylib->installName().equals(installName)) + return true; + } + return false; +} + +ArchHandler &MachOLinkingContext::archHandler() const { + if (!_archHandler) + _archHandler = ArchHandler::create(_arch); + return *_archHandler; +} + + +void MachOLinkingContext::addSectionAlignment(StringRef seg, StringRef sect, + uint8_t align2) { + SectionAlign entry; + entry.segmentName = seg; + entry.sectionName = sect; + entry.align2 = align2; + _sectAligns.push_back(entry); +} + +bool MachOLinkingContext::sectionAligned(StringRef seg, StringRef sect, + uint8_t &align2) const { + for (const SectionAlign &entry : _sectAligns) { + if (seg.equals(entry.segmentName) && sect.equals(entry.sectionName)) { + align2 = entry.align2; + return true; + } + } + return false; +} + + +void MachOLinkingContext::addExportSymbol(StringRef sym) { + // Support old crufty export lists with bogus entries. + if (sym.endswith(".eh") || sym.startswith(".objc_category_name_")) { + llvm::errs() << "warning: ignoring " << sym << " in export list\n"; + return; + } + // Only i386 MacOSX uses old ABI, so don't change those. + if ((_os != OS::macOSX) || (_arch != arch_x86)) { + // ObjC has two differnent ABIs. Be nice and allow one export list work for + // both ABIs by renaming symbols. + if (sym.startswith(".objc_class_name_")) { + std::string abi2className("_OBJC_CLASS_$_"); + abi2className += sym.substr(17); + _exportedSymbols.insert(copy(abi2className)); + std::string abi2metaclassName("_OBJC_METACLASS_$_"); + abi2metaclassName += sym.substr(17); + _exportedSymbols.insert(copy(abi2metaclassName)); + return; + } + } + + // FIXME: Support wildcards. + _exportedSymbols.insert(sym); +} + +bool MachOLinkingContext::exportSymbolNamed(StringRef sym) const { + switch (_exportMode) { + case ExportMode::globals: + llvm_unreachable("exportSymbolNamed() should not be called in this mode"); + break; + case ExportMode::whiteList: + return _exportedSymbols.count(sym); + case ExportMode::blackList: + return !_exportedSymbols.count(sym); + } + llvm_unreachable("_exportMode unknown enum value"); +} + +std::string MachOLinkingContext::demangle(StringRef symbolName) const { + // Only try to demangle symbols if -demangle on command line + if (!demangleSymbols()) + return symbolName; + + // Only try to demangle symbols that look like C++ symbols + if (!symbolName.startswith("__Z")) + return symbolName; + +#if defined(HAVE_CXXABI_H) + SmallString<256> symBuff; + StringRef nullTermSym = Twine(symbolName).toNullTerminatedStringRef(symBuff); + // Mach-O has extra leading underscore that needs to be removed. + const char *cstr = nullTermSym.data() + 1; + int status; + char *demangled = abi::__cxa_demangle(cstr, nullptr, nullptr, &status); + if (demangled != NULL) { + std::string result(demangled); + // __cxa_demangle() always uses a malloc'ed buffer to return the result. + free(demangled); + return result; + } +#endif + + return symbolName; +} + +std::error_code MachOLinkingContext::createDependencyFile(StringRef path) { + std::error_code ec; + _dependencyInfo = std::unique_ptr<llvm::raw_fd_ostream>(new + llvm::raw_fd_ostream(path, ec, llvm::sys::fs::F_None)); + if (ec) { + _dependencyInfo.reset(); + return ec; + } + + char linkerVersionOpcode = 0x00; + *_dependencyInfo << linkerVersionOpcode; + *_dependencyInfo << "lld"; // FIXME + *_dependencyInfo << '\0'; + + return std::error_code(); +} + +void MachOLinkingContext::addInputFileDependency(StringRef path) const { + if (!_dependencyInfo) + return; + + char inputFileOpcode = 0x10; + *_dependencyInfo << inputFileOpcode; + *_dependencyInfo << path; + *_dependencyInfo << '\0'; +} + +void MachOLinkingContext::addInputFileNotFound(StringRef path) const { + if (!_dependencyInfo) + return; + + char inputFileOpcode = 0x11; + *_dependencyInfo << inputFileOpcode; + *_dependencyInfo << path; + *_dependencyInfo << '\0'; +} + +void MachOLinkingContext::addOutputFileDependency(StringRef path) const { + if (!_dependencyInfo) + return; + + char outputFileOpcode = 0x40; + *_dependencyInfo << outputFileOpcode; + *_dependencyInfo << path; + *_dependencyInfo << '\0'; +} + +void MachOLinkingContext::appendOrderedSymbol(StringRef symbol, + StringRef filename) { + // To support sorting static functions which may have the same name in + // multiple .o files, _orderFiles maps the symbol name to a vector + // of OrderFileNode each of which can specify a file prefix. + OrderFileNode info; + if (!filename.empty()) + info.fileFilter = copy(filename); + info.order = _orderFileEntries++; + _orderFiles[symbol].push_back(info); +} + +bool +MachOLinkingContext::findOrderOrdinal(const std::vector<OrderFileNode> &nodes, + const DefinedAtom *atom, + unsigned &ordinal) { + const File *objFile = &atom->file(); + assert(objFile); + StringRef objName = objFile->path(); + std::pair<StringRef, StringRef> dirAndLeaf = objName.rsplit('/'); + if (!dirAndLeaf.second.empty()) + objName = dirAndLeaf.second; + for (const OrderFileNode &info : nodes) { + if (info.fileFilter.empty()) { + // Have unprefixed symbol name in order file that matches this atom. + ordinal = info.order; + return true; + } + if (info.fileFilter.equals(objName)) { + // Have prefixed symbol name in order file that matches atom's path. + ordinal = info.order; + return true; + } + } + return false; +} + +bool MachOLinkingContext::customAtomOrderer(const DefinedAtom *left, + const DefinedAtom *right, + bool &leftBeforeRight) const { + // No custom sorting if no order file entries. + if (!_orderFileEntries) + return false; + + // Order files can only order named atoms. + StringRef leftName = left->name(); + StringRef rightName = right->name(); + if (leftName.empty() || rightName.empty()) + return false; + + // If neither is in order file list, no custom sorter. + auto leftPos = _orderFiles.find(leftName); + auto rightPos = _orderFiles.find(rightName); + bool leftIsOrdered = (leftPos != _orderFiles.end()); + bool rightIsOrdered = (rightPos != _orderFiles.end()); + if (!leftIsOrdered && !rightIsOrdered) + return false; + + // There could be multiple symbols with same name but different file prefixes. + unsigned leftOrder; + unsigned rightOrder; + bool foundLeft = + leftIsOrdered && findOrderOrdinal(leftPos->getValue(), left, leftOrder); + bool foundRight = rightIsOrdered && + findOrderOrdinal(rightPos->getValue(), right, rightOrder); + if (!foundLeft && !foundRight) + return false; + + // If only one is in order file list, ordered one goes first. + if (foundLeft != foundRight) + leftBeforeRight = foundLeft; + else + leftBeforeRight = (leftOrder < rightOrder); + + return true; +} + +static bool isLibrary(const std::unique_ptr<Node> &elem) { + if (FileNode *node = dyn_cast<FileNode>(const_cast<Node *>(elem.get()))) { + File *file = node->getFile(); + return isa<SharedLibraryFile>(file) || isa<ArchiveLibraryFile>(file); + } + return false; +} + +// The darwin linker processes input files in two phases. The first phase +// links in all object (.o) files in command line order. The second phase +// links in libraries in command line order. +// In this function we reorder the input files so that all the object files +// comes before any library file. We also make a group for the library files +// so that the Resolver will reiterate over the libraries as long as we find +// new undefines from libraries. +void MachOLinkingContext::finalizeInputFiles() { + std::vector<std::unique_ptr<Node>> &elements = getNodes(); + std::stable_sort(elements.begin(), elements.end(), + [](const std::unique_ptr<Node> &a, + const std::unique_ptr<Node> &b) { + return !isLibrary(a) && isLibrary(b); + }); + size_t numLibs = std::count_if(elements.begin(), elements.end(), isLibrary); + elements.push_back(llvm::make_unique<GroupEnd>(numLibs)); +} + +} // end namespace lld diff --git a/lib/ReaderWriter/MachO/MachONormalizedFile.h b/lib/ReaderWriter/MachO/MachONormalizedFile.h new file mode 100644 index 0000000000000..70bcde2dea227 --- /dev/null +++ b/lib/ReaderWriter/MachO/MachONormalizedFile.h @@ -0,0 +1,323 @@ +//===- lib/ReaderWriter/MachO/MachONormalizedFile.h -----------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +/// +/// \file These data structures comprise the "normalized" view of +/// mach-o object files. The normalized view is an in-memory only data structure +/// which is always in native endianness and pointer size. +/// +/// The normalized view easily converts to and from YAML using YAML I/O. +/// +/// The normalized view converts to and from binary mach-o object files using +/// the writeBinary() and readBinary() functions. +/// +/// The normalized view converts to and from lld::Atoms using the +/// normalizedToAtoms() and normalizedFromAtoms(). +/// +/// Overall, the conversion paths available look like: +/// +/// +---------------+ +/// | binary mach-o | +/// +---------------+ +/// ^ +/// | +/// v +/// +------------+ +------+ +/// | normalized | <-> | yaml | +/// +------------+ +------+ +/// ^ +/// | +/// v +/// +-------+ +/// | Atoms | +/// +-------+ +/// + +#include "lld/Core/Error.h" +#include "lld/Core/LLVM.h" +#include "lld/ReaderWriter/MachOLinkingContext.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/MachO.h" +#include "llvm/Support/YAMLTraits.h" + +#ifndef LLD_READER_WRITER_MACHO_NORMALIZE_FILE_H +#define LLD_READER_WRITER_MACHO_NORMALIZE_FILE_H + +using llvm::BumpPtrAllocator; +using llvm::yaml::Hex64; +using llvm::yaml::Hex32; +using llvm::yaml::Hex16; +using llvm::yaml::Hex8; +using llvm::yaml::SequenceTraits; +using llvm::MachO::HeaderFileType; +using llvm::MachO::BindType; +using llvm::MachO::RebaseType; +using llvm::MachO::NListType; +using llvm::MachO::RelocationInfoType; +using llvm::MachO::SectionType; +using llvm::MachO::LoadCommandType; +using llvm::MachO::ExportSymbolKind; +using llvm::MachO::DataRegionType; + +namespace lld { +namespace mach_o { +namespace normalized { + + +/// The real mach-o relocation record is 8-bytes on disk and is +/// encoded in one of two different bit-field patterns. This +/// normalized form has the union of all possible fields. +struct Relocation { + Relocation() : offset(0), scattered(false), + type(llvm::MachO::GENERIC_RELOC_VANILLA), + length(0), pcRel(false), isExtern(false), value(0), + symbol(0) { } + + Hex32 offset; + bool scattered; + RelocationInfoType type; + uint8_t length; + bool pcRel; + bool isExtern; + Hex32 value; + uint32_t symbol; +}; + +/// A typedef so that YAML I/O can treat this vector as a sequence. +typedef std::vector<Relocation> Relocations; + +/// A typedef so that YAML I/O can process the raw bytes in a section. +typedef std::vector<Hex8> ContentBytes; + +/// A typedef so that YAML I/O can treat indirect symbols as a flow sequence. +typedef std::vector<uint32_t> IndirectSymbols; + +/// A typedef so that YAML I/O can encode/decode section attributes. +LLVM_YAML_STRONG_TYPEDEF(uint32_t, SectionAttr) + +/// Mach-O has a 32-bit and 64-bit section record. This normalized form +/// can support either kind. +struct Section { + Section() : type(llvm::MachO::S_REGULAR), + attributes(0), alignment(0), address(0) { } + + StringRef segmentName; + StringRef sectionName; + SectionType type; + SectionAttr attributes; + uint32_t alignment; + Hex64 address; + ArrayRef<uint8_t> content; + Relocations relocations; + IndirectSymbols indirectSymbols; +}; + + +/// A typedef so that YAML I/O can encode/decode the scope bits of an nlist. +LLVM_YAML_STRONG_TYPEDEF(uint8_t, SymbolScope) + +/// A typedef so that YAML I/O can encode/decode the desc bits of an nlist. +LLVM_YAML_STRONG_TYPEDEF(uint16_t, SymbolDesc) + +/// Mach-O has a 32-bit and 64-bit symbol table entry (nlist), and the symbol +/// type and scope and mixed in the same n_type field. This normalized form +/// works for any pointer size and separates out the type and scope. +struct Symbol { + Symbol() : type(llvm::MachO::N_UNDF), scope(0), sect(0), desc(0), value(0) { } + + StringRef name; + NListType type; + SymbolScope scope; + uint8_t sect; + SymbolDesc desc; + Hex64 value; +}; + +/// A typedef so that YAML I/O can (de/en)code the protection bits of a segment. +LLVM_YAML_STRONG_TYPEDEF(uint32_t, VMProtect) + +/// A typedef to hold verions X.Y.X packed into 32-bit xxxx.yy.zz +LLVM_YAML_STRONG_TYPEDEF(uint32_t, PackedVersion) + +/// Segments are only used in normalized final linked images (not in relocatable +/// object files). They specify how a range of the file is loaded. +struct Segment { + StringRef name; + Hex64 address; + Hex64 size; + VMProtect access; +}; + +/// Only used in normalized final linked images to specify on which dylibs +/// it depends. +struct DependentDylib { + StringRef path; + LoadCommandType kind; + PackedVersion compatVersion; + PackedVersion currentVersion; +}; + +/// A normalized rebasing entry. Only used in normalized final linked images. +struct RebaseLocation { + Hex32 segOffset; + uint8_t segIndex; + RebaseType kind; +}; + +/// A normalized binding entry. Only used in normalized final linked images. +struct BindLocation { + Hex32 segOffset; + uint8_t segIndex; + BindType kind; + bool canBeNull; + int ordinal; + StringRef symbolName; + Hex64 addend; +}; + +/// A typedef so that YAML I/O can encode/decode export flags. +LLVM_YAML_STRONG_TYPEDEF(uint32_t, ExportFlags) + +/// A normalized export entry. Only used in normalized final linked images. +struct Export { + StringRef name; + Hex64 offset; + ExportSymbolKind kind; + ExportFlags flags; + Hex32 otherOffset; + StringRef otherName; +}; + +/// A normalized data-in-code entry. +struct DataInCode { + Hex32 offset; + Hex16 length; + DataRegionType kind; +}; + + +/// A typedef so that YAML I/O can encode/decode mach_header.flags. +LLVM_YAML_STRONG_TYPEDEF(uint32_t, FileFlags) + +/// +struct NormalizedFile { + NormalizedFile() : arch(MachOLinkingContext::arch_unknown), + fileType(llvm::MachO::MH_OBJECT), + flags(0), + hasUUID(false), + os(MachOLinkingContext::OS::unknown) { } + + MachOLinkingContext::Arch arch; + HeaderFileType fileType; + FileFlags flags; + std::vector<Segment> segments; // Not used in object files. + std::vector<Section> sections; + + // Symbols sorted by kind. + std::vector<Symbol> localSymbols; + std::vector<Symbol> globalSymbols; + std::vector<Symbol> undefinedSymbols; + + // Maps to load commands with no LINKEDIT content (final linked images only). + std::vector<DependentDylib> dependentDylibs; + StringRef installName; // dylibs only + PackedVersion compatVersion; // dylibs only + PackedVersion currentVersion; // dylibs only + bool hasUUID; + std::vector<StringRef> rpaths; + Hex64 entryAddress; + MachOLinkingContext::OS os; + Hex64 sourceVersion; + PackedVersion minOSverson; + PackedVersion sdkVersion; + + // Maps to load commands with LINKEDIT content (final linked images only). + Hex32 pageSize; + std::vector<RebaseLocation> rebasingInfo; + std::vector<BindLocation> bindingInfo; + std::vector<BindLocation> weakBindingInfo; + std::vector<BindLocation> lazyBindingInfo; + std::vector<Export> exportInfo; + std::vector<DataInCode> dataInCode; + + // TODO: + // code-signature + // split-seg-info + // function-starts + + // For any allocations in this struct which need to be owned by this struct. + BumpPtrAllocator ownedAllocations; +}; + +/// Tests if a file is a non-fat mach-o object file. +bool isThinObjectFile(StringRef path, MachOLinkingContext::Arch &arch); + +/// If the buffer is a fat file with the request arch, then this function +/// returns true with 'offset' and 'size' set to location of the arch slice +/// within the buffer. Otherwise returns false; +bool sliceFromFatFile(const MemoryBuffer &mb, MachOLinkingContext::Arch arch, + uint32_t &offset, uint32_t &size); + +/// Reads a mach-o file and produces an in-memory normalized view. +ErrorOr<std::unique_ptr<NormalizedFile>> +readBinary(std::unique_ptr<MemoryBuffer> &mb, + const MachOLinkingContext::Arch arch); + +/// Takes in-memory normalized view and writes a mach-o object file. +std::error_code writeBinary(const NormalizedFile &file, StringRef path); + +size_t headerAndLoadCommandsSize(const NormalizedFile &file); + + +/// Parses a yaml encoded mach-o file to produce an in-memory normalized view. +ErrorOr<std::unique_ptr<NormalizedFile>> +readYaml(std::unique_ptr<MemoryBuffer> &mb); + +/// Writes a yaml encoded mach-o files given an in-memory normalized view. +std::error_code writeYaml(const NormalizedFile &file, raw_ostream &out); + +std::error_code +normalizedObjectToAtoms(MachOFile *file, + const NormalizedFile &normalizedFile, + bool copyRefs); + +std::error_code +normalizedDylibToAtoms(MachODylibFile *file, + const NormalizedFile &normalizedFile, + bool copyRefs); + +/// Takes in-memory normalized dylib or object and parses it into lld::File +ErrorOr<std::unique_ptr<lld::File>> +normalizedToAtoms(const NormalizedFile &normalizedFile, StringRef path, + bool copyRefs); + +/// Takes atoms and generates a normalized macho-o view. +ErrorOr<std::unique_ptr<NormalizedFile>> +normalizedFromAtoms(const lld::File &atomFile, const MachOLinkingContext &ctxt); + + +} // namespace normalized + +/// Class for interfacing mach-o yaml files into generic yaml parsing +class MachOYamlIOTaggedDocumentHandler : public YamlIOTaggedDocumentHandler { +public: + MachOYamlIOTaggedDocumentHandler(MachOLinkingContext::Arch arch) + : _arch(arch) { } + bool handledDocTag(llvm::yaml::IO &io, const lld::File *&file) const override; +private: + const MachOLinkingContext::Arch _arch; +}; + +} // namespace mach_o +} // namespace lld + +#endif // LLD_READER_WRITER_MACHO_NORMALIZE_FILE_H diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp new file mode 100644 index 0000000000000..07a6dbfe569b0 --- /dev/null +++ b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp @@ -0,0 +1,582 @@ +//===- lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp ---------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +/// +/// \file For mach-o object files, this implementation converts from +/// mach-o on-disk binary format to in-memory normalized mach-o. +/// +/// +---------------+ +/// | binary mach-o | +/// +---------------+ +/// | +/// | +/// v +/// +------------+ +/// | normalized | +/// +------------+ + +#include "MachONormalizedFile.h" +#include "ArchHandler.h" +#include "MachONormalizedFileBinaryUtils.h" +#include "lld/Core/Error.h" +#include "lld/Core/LLVM.h" +#include "lld/Core/SharedLibraryFile.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Object/MachO.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/MachO.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include <functional> +#include <system_error> + +using namespace llvm::MachO; +using llvm::object::ExportEntry; +using llvm::object::MachOObjectFile; + +namespace lld { +namespace mach_o { +namespace normalized { + +// Utility to call a lambda expression on each load command. +static std::error_code forEachLoadCommand( + StringRef lcRange, unsigned lcCount, bool isBig, bool is64, + std::function<bool(uint32_t cmd, uint32_t size, const char *lc)> func) { + const char* p = lcRange.begin(); + for (unsigned i=0; i < lcCount; ++i) { + const load_command *lc = reinterpret_cast<const load_command*>(p); + load_command lcCopy; + const load_command *slc = lc; + if (isBig != llvm::sys::IsBigEndianHost) { + memcpy(&lcCopy, lc, sizeof(load_command)); + swapStruct(lcCopy); + slc = &lcCopy; + } + if ( (p + slc->cmdsize) > lcRange.end() ) + return make_error_code(llvm::errc::executable_format_error); + + if (func(slc->cmd, slc->cmdsize, p)) + return std::error_code(); + + p += slc->cmdsize; + } + + return std::error_code(); +} + +static std::error_code appendRelocations(Relocations &relocs, StringRef buffer, + bool bigEndian, + uint32_t reloff, uint32_t nreloc) { + if ((reloff + nreloc*8) > buffer.size()) + return make_error_code(llvm::errc::executable_format_error); + const any_relocation_info* relocsArray = + reinterpret_cast<const any_relocation_info*>(buffer.begin()+reloff); + + for(uint32_t i=0; i < nreloc; ++i) { + relocs.push_back(unpackRelocation(relocsArray[i], bigEndian)); + } + return std::error_code(); +} + +static std::error_code +appendIndirectSymbols(IndirectSymbols &isyms, StringRef buffer, bool isBig, + uint32_t istOffset, uint32_t istCount, + uint32_t startIndex, uint32_t count) { + if ((istOffset + istCount*4) > buffer.size()) + return make_error_code(llvm::errc::executable_format_error); + if (startIndex+count > istCount) + return make_error_code(llvm::errc::executable_format_error); + const uint8_t *indirectSymbolArray = (const uint8_t *)buffer.data(); + + for(uint32_t i=0; i < count; ++i) { + isyms.push_back(read32( + indirectSymbolArray + (startIndex + i) * sizeof(uint32_t), isBig)); + } + return std::error_code(); +} + + +template <typename T> static T readBigEndian(T t) { + if (llvm::sys::IsLittleEndianHost) + llvm::sys::swapByteOrder(t); + return t; +} + + +static bool isMachOHeader(const mach_header *mh, bool &is64, bool &isBig) { + switch (read32(&mh->magic, false)) { + case llvm::MachO::MH_MAGIC: + is64 = false; + isBig = false; + return true; + case llvm::MachO::MH_MAGIC_64: + is64 = true; + isBig = false; + return true; + case llvm::MachO::MH_CIGAM: + is64 = false; + isBig = true; + return true; + case llvm::MachO::MH_CIGAM_64: + is64 = true; + isBig = true; + return true; + default: + return false; + } +} + + +bool isThinObjectFile(StringRef path, MachOLinkingContext::Arch &arch) { + // Try opening and mapping file at path. + ErrorOr<std::unique_ptr<MemoryBuffer>> b = MemoryBuffer::getFileOrSTDIN(path); + if (b.getError()) + return false; + + // If file length < 32 it is too small to be mach-o object file. + StringRef fileBuffer = b->get()->getBuffer(); + if (fileBuffer.size() < 32) + return false; + + // If file buffer does not start with MH_MAGIC (and variants), not obj file. + const mach_header *mh = reinterpret_cast<const mach_header *>( + fileBuffer.begin()); + bool is64, isBig; + if (!isMachOHeader(mh, is64, isBig)) + return false; + + // If not MH_OBJECT, not object file. + if (read32(&mh->filetype, isBig) != MH_OBJECT) + return false; + + // Lookup up arch from cpu/subtype pair. + arch = MachOLinkingContext::archFromCpuType( + read32(&mh->cputype, isBig), + read32(&mh->cpusubtype, isBig)); + return true; +} + + +bool sliceFromFatFile(const MemoryBuffer &mb, MachOLinkingContext::Arch arch, + uint32_t &offset, uint32_t &size) { + const char *start = mb.getBufferStart(); + const llvm::MachO::fat_header *fh = + reinterpret_cast<const llvm::MachO::fat_header *>(start); + if (readBigEndian(fh->magic) != llvm::MachO::FAT_MAGIC) + return false; + uint32_t nfat_arch = readBigEndian(fh->nfat_arch); + const fat_arch *fstart = + reinterpret_cast<const fat_arch *>(start + sizeof(fat_header)); + const fat_arch *fend = + reinterpret_cast<const fat_arch *>(start + sizeof(fat_header) + + sizeof(fat_arch) * nfat_arch); + const uint32_t reqCpuType = MachOLinkingContext::cpuTypeFromArch(arch); + const uint32_t reqCpuSubtype = MachOLinkingContext::cpuSubtypeFromArch(arch); + for (const fat_arch *fa = fstart; fa < fend; ++fa) { + if ((readBigEndian(fa->cputype) == reqCpuType) && + (readBigEndian(fa->cpusubtype) == reqCpuSubtype)) { + offset = readBigEndian(fa->offset); + size = readBigEndian(fa->size); + if ((offset + size) > mb.getBufferSize()) + return false; + return true; + } + } + return false; +} + +/// Reads a mach-o file and produces an in-memory normalized view. +ErrorOr<std::unique_ptr<NormalizedFile>> +readBinary(std::unique_ptr<MemoryBuffer> &mb, + const MachOLinkingContext::Arch arch) { + // Make empty NormalizedFile. + std::unique_ptr<NormalizedFile> f(new NormalizedFile()); + + const char *start = mb->getBufferStart(); + size_t objSize = mb->getBufferSize(); + const mach_header *mh = reinterpret_cast<const mach_header *>(start); + + uint32_t sliceOffset; + uint32_t sliceSize; + if (sliceFromFatFile(*mb, arch, sliceOffset, sliceSize)) { + start = &start[sliceOffset]; + objSize = sliceSize; + mh = reinterpret_cast<const mach_header *>(start); + } + + // Determine endianness and pointer size for mach-o file. + bool is64, isBig; + if (!isMachOHeader(mh, is64, isBig)) + return make_error_code(llvm::errc::executable_format_error); + + // Endian swap header, if needed. + mach_header headerCopy; + const mach_header *smh = mh; + if (isBig != llvm::sys::IsBigEndianHost) { + memcpy(&headerCopy, mh, sizeof(mach_header)); + swapStruct(headerCopy); + smh = &headerCopy; + } + + // Validate head and load commands fit in buffer. + const uint32_t lcCount = smh->ncmds; + const char *lcStart = + start + (is64 ? sizeof(mach_header_64) : sizeof(mach_header)); + StringRef lcRange(lcStart, smh->sizeofcmds); + if (lcRange.end() > (start + objSize)) + return make_error_code(llvm::errc::executable_format_error); + + // Get architecture from mach_header. + f->arch = MachOLinkingContext::archFromCpuType(smh->cputype, smh->cpusubtype); + if (f->arch != arch) { + return make_dynamic_error_code(Twine("file is wrong architecture. Expected " + "(" + MachOLinkingContext::nameFromArch(arch) + + ") found (" + + MachOLinkingContext::nameFromArch(f->arch) + + ")" )); + } + // Copy file type and flags + f->fileType = HeaderFileType(smh->filetype); + f->flags = smh->flags; + + + // Pre-scan load commands looking for indirect symbol table. + uint32_t indirectSymbolTableOffset = 0; + uint32_t indirectSymbolTableCount = 0; + std::error_code ec = forEachLoadCommand(lcRange, lcCount, isBig, is64, + [&](uint32_t cmd, uint32_t size, + const char *lc) -> bool { + if (cmd == LC_DYSYMTAB) { + const dysymtab_command *d = reinterpret_cast<const dysymtab_command*>(lc); + indirectSymbolTableOffset = read32(&d->indirectsymoff, isBig); + indirectSymbolTableCount = read32(&d->nindirectsyms, isBig); + return true; + } + return false; + }); + if (ec) + return ec; + + // Walk load commands looking for segments/sections and the symbol table. + const data_in_code_entry *dataInCode = nullptr; + const dyld_info_command *dyldInfo = nullptr; + uint32_t dataInCodeSize = 0; + ec = forEachLoadCommand(lcRange, lcCount, isBig, is64, + [&] (uint32_t cmd, uint32_t size, const char* lc) -> bool { + switch(cmd) { + case LC_SEGMENT_64: + if (is64) { + const segment_command_64 *seg = + reinterpret_cast<const segment_command_64*>(lc); + const unsigned sectionCount = read32(&seg->nsects, isBig); + const section_64 *sects = reinterpret_cast<const section_64*> + (lc + sizeof(segment_command_64)); + const unsigned lcSize = sizeof(segment_command_64) + + sectionCount*sizeof(section_64); + // Verify sections don't extend beyond end of segment load command. + if (lcSize > size) + return true; + for (unsigned i=0; i < sectionCount; ++i) { + const section_64 *sect = §s[i]; + Section section; + section.segmentName = getString16(sect->segname); + section.sectionName = getString16(sect->sectname); + section.type = (SectionType)(read32(§->flags, isBig) & + SECTION_TYPE); + section.attributes = read32(§->flags, isBig) & SECTION_ATTRIBUTES; + section.alignment = read32(§->align, isBig); + section.address = read64(§->addr, isBig); + const uint8_t *content = + (const uint8_t *)start + read32(§->offset, isBig); + size_t contentSize = read64(§->size, isBig); + // Note: this assign() is copying the content bytes. Ideally, + // we can use a custom allocator for vector to avoid the copy. + section.content = llvm::makeArrayRef(content, contentSize); + appendRelocations(section.relocations, mb->getBuffer(), isBig, + read32(§->reloff, isBig), + read32(§->nreloc, isBig)); + if (section.type == S_NON_LAZY_SYMBOL_POINTERS) { + appendIndirectSymbols(section.indirectSymbols, mb->getBuffer(), + isBig, + indirectSymbolTableOffset, + indirectSymbolTableCount, + read32(§->reserved1, isBig), + contentSize/4); + } + f->sections.push_back(section); + } + } + break; + case LC_SEGMENT: + if (!is64) { + const segment_command *seg = + reinterpret_cast<const segment_command*>(lc); + const unsigned sectionCount = read32(&seg->nsects, isBig); + const section *sects = reinterpret_cast<const section*> + (lc + sizeof(segment_command)); + const unsigned lcSize = sizeof(segment_command) + + sectionCount*sizeof(section); + // Verify sections don't extend beyond end of segment load command. + if (lcSize > size) + return true; + for (unsigned i=0; i < sectionCount; ++i) { + const section *sect = §s[i]; + Section section; + section.segmentName = getString16(sect->segname); + section.sectionName = getString16(sect->sectname); + section.type = (SectionType)(read32(§->flags, isBig) & + SECTION_TYPE); + section.attributes = + read32((const uint8_t *)§->flags, isBig) & SECTION_ATTRIBUTES; + section.alignment = read32(§->align, isBig); + section.address = read32(§->addr, isBig); + const uint8_t *content = + (const uint8_t *)start + read32(§->offset, isBig); + size_t contentSize = read32(§->size, isBig); + // Note: this assign() is copying the content bytes. Ideally, + // we can use a custom allocator for vector to avoid the copy. + section.content = llvm::makeArrayRef(content, contentSize); + appendRelocations(section.relocations, mb->getBuffer(), isBig, + read32(§->reloff, isBig), + read32(§->nreloc, isBig)); + if (section.type == S_NON_LAZY_SYMBOL_POINTERS) { + appendIndirectSymbols( + section.indirectSymbols, mb->getBuffer(), isBig, + indirectSymbolTableOffset, indirectSymbolTableCount, + read32(§->reserved1, isBig), contentSize / 4); + } + f->sections.push_back(section); + } + } + break; + case LC_SYMTAB: { + const symtab_command *st = reinterpret_cast<const symtab_command*>(lc); + const char *strings = start + read32(&st->stroff, isBig); + const uint32_t strSize = read32(&st->strsize, isBig); + // Validate string pool and symbol table all in buffer. + if (read32((const uint8_t *)&st->stroff, isBig) + + read32((const uint8_t *)&st->strsize, isBig) > + objSize) + return true; + if (is64) { + const uint32_t symOffset = read32(&st->symoff, isBig); + const uint32_t symCount = read32(&st->nsyms, isBig); + if ( symOffset+(symCount*sizeof(nlist_64)) > objSize) + return true; + const nlist_64 *symbols = + reinterpret_cast<const nlist_64 *>(start + symOffset); + // Convert each nlist_64 to a lld::mach_o::normalized::Symbol. + for(uint32_t i=0; i < symCount; ++i) { + const nlist_64 *sin = &symbols[i]; + nlist_64 tempSym; + if (isBig != llvm::sys::IsBigEndianHost) { + tempSym = *sin; swapStruct(tempSym); sin = &tempSym; + } + Symbol sout; + if (sin->n_strx > strSize) + return true; + sout.name = &strings[sin->n_strx]; + sout.type = (NListType)(sin->n_type & N_TYPE); + sout.scope = (sin->n_type & (N_PEXT|N_EXT)); + sout.sect = sin->n_sect; + sout.desc = sin->n_desc; + sout.value = sin->n_value; + if (sout.type == N_UNDF) + f->undefinedSymbols.push_back(sout); + else if (sin->n_type & N_EXT) + f->globalSymbols.push_back(sout); + else + f->localSymbols.push_back(sout); + } + } else { + const uint32_t symOffset = read32(&st->symoff, isBig); + const uint32_t symCount = read32(&st->nsyms, isBig); + if ( symOffset+(symCount*sizeof(nlist)) > objSize) + return true; + const nlist *symbols = + reinterpret_cast<const nlist *>(start + symOffset); + // Convert each nlist to a lld::mach_o::normalized::Symbol. + for(uint32_t i=0; i < symCount; ++i) { + const nlist *sin = &symbols[i]; + nlist tempSym; + if (isBig != llvm::sys::IsBigEndianHost) { + tempSym = *sin; swapStruct(tempSym); sin = &tempSym; + } + Symbol sout; + if (sin->n_strx > strSize) + return true; + sout.name = &strings[sin->n_strx]; + sout.type = (NListType)(sin->n_type & N_TYPE); + sout.scope = (sin->n_type & (N_PEXT|N_EXT)); + sout.sect = sin->n_sect; + sout.desc = sin->n_desc; + sout.value = sin->n_value; + if (sout.type == N_UNDF) + f->undefinedSymbols.push_back(sout); + else if (sout.scope == (SymbolScope)N_EXT) + f->globalSymbols.push_back(sout); + else + f->localSymbols.push_back(sout); + } + } + } + break; + case LC_ID_DYLIB: { + const dylib_command *dl = reinterpret_cast<const dylib_command*>(lc); + f->installName = lc + read32(&dl->dylib.name, isBig); + f->currentVersion = read32(&dl->dylib.current_version, isBig); + f->compatVersion = read32(&dl->dylib.compatibility_version, isBig); + } + break; + case LC_DATA_IN_CODE: { + const linkedit_data_command *ldc = + reinterpret_cast<const linkedit_data_command*>(lc); + dataInCode = reinterpret_cast<const data_in_code_entry *>( + start + read32(&ldc->dataoff, isBig)); + dataInCodeSize = read32(&ldc->datasize, isBig); + } + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: { + const dylib_command *dl = reinterpret_cast<const dylib_command*>(lc); + DependentDylib entry; + entry.path = lc + read32(&dl->dylib.name, isBig); + entry.kind = LoadCommandType(cmd); + entry.compatVersion = read32(&dl->dylib.compatibility_version, isBig); + entry.currentVersion = read32(&dl->dylib.current_version, isBig); + f->dependentDylibs.push_back(entry); + } + break; + case LC_RPATH: { + const rpath_command *rpc = reinterpret_cast<const rpath_command *>(lc); + f->rpaths.push_back(lc + read32(&rpc->path, isBig)); + } + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + dyldInfo = reinterpret_cast<const dyld_info_command*>(lc); + break; + } + return false; + }); + if (ec) + return ec; + + if (dataInCode) { + // Convert on-disk data_in_code_entry array to DataInCode vector. + for (unsigned i=0; i < dataInCodeSize/sizeof(data_in_code_entry); ++i) { + DataInCode entry; + entry.offset = read32(&dataInCode[i].offset, isBig); + entry.length = read16(&dataInCode[i].length, isBig); + entry.kind = + (DataRegionType)read16((const uint8_t *)&dataInCode[i].kind, isBig); + f->dataInCode.push_back(entry); + } + } + + if (dyldInfo) { + // If any exports, extract and add to normalized exportInfo vector. + if (dyldInfo->export_size) { + const uint8_t *trieStart = reinterpret_cast<const uint8_t*>(start + + dyldInfo->export_off); + ArrayRef<uint8_t> trie(trieStart, dyldInfo->export_size); + for (const ExportEntry &trieExport : MachOObjectFile::exports(trie)) { + Export normExport; + normExport.name = trieExport.name().copy(f->ownedAllocations); + normExport.offset = trieExport.address(); + normExport.kind = ExportSymbolKind(trieExport.flags() & EXPORT_SYMBOL_FLAGS_KIND_MASK); + normExport.flags = trieExport.flags() & ~EXPORT_SYMBOL_FLAGS_KIND_MASK; + normExport.otherOffset = trieExport.other(); + if (!trieExport.otherName().empty()) + normExport.otherName = trieExport.otherName().copy(f->ownedAllocations); + f->exportInfo.push_back(normExport); + } + } + } + + return std::move(f); +} + +class MachOObjectReader : public Reader { +public: + MachOObjectReader(MachOLinkingContext &ctx) : _ctx(ctx) {} + + bool canParse(file_magic magic, StringRef ext, + const MemoryBuffer &mb) const override { + switch (magic) { + case llvm::sys::fs::file_magic::macho_object: + return (mb.getBufferSize() > 32); + default: + return false; + } + } + + std::error_code + loadFile(std::unique_ptr<MemoryBuffer> mb, const Registry ®istry, + std::vector<std::unique_ptr<File>> &result) const override { + auto *file = new MachOFile(std::move(mb), &_ctx); + result.push_back(std::unique_ptr<MachOFile>(file)); + return std::error_code(); + } + +private: + MachOLinkingContext &_ctx; +}; + +class MachODylibReader : public Reader { +public: + MachODylibReader(MachOLinkingContext &ctx) : _ctx(ctx) {} + + bool canParse(file_magic magic, StringRef ext, + const MemoryBuffer &mb) const override { + switch (magic) { + case llvm::sys::fs::file_magic::macho_dynamically_linked_shared_lib: + case llvm::sys::fs::file_magic::macho_dynamically_linked_shared_lib_stub: + return (mb.getBufferSize() > 32); + default: + return false; + } + } + + std::error_code + loadFile(std::unique_ptr<MemoryBuffer> mb, const Registry ®istry, + std::vector<std::unique_ptr<File>> &result) const override { + auto *file = new MachODylibFile(std::move(mb), &_ctx); + result.push_back(std::unique_ptr<MachODylibFile>(file)); + return std::error_code(); + } + +private: + MachOLinkingContext &_ctx; +}; + +} // namespace normalized +} // namespace mach_o + +void Registry::addSupportMachOObjects(MachOLinkingContext &ctx) { + MachOLinkingContext::Arch arch = ctx.arch(); + add(std::unique_ptr<Reader>(new mach_o::normalized::MachOObjectReader(ctx))); + add(std::unique_ptr<Reader>(new mach_o::normalized::MachODylibReader(ctx))); + addKindTable(Reference::KindNamespace::mach_o, ctx.archHandler().kindArch(), + ctx.archHandler().kindStrings()); + add(std::unique_ptr<YamlIOTaggedDocumentHandler>( + new mach_o::MachOYamlIOTaggedDocumentHandler(arch))); +} + + +} // namespace lld diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h new file mode 100644 index 0000000000000..613c1b2f251ab --- /dev/null +++ b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h @@ -0,0 +1,177 @@ +//===- lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h ------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "MachONormalizedFile.h" +#include "lld/Core/Error.h" +#include "lld/Core/LLVM.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/MachO.h" +#include <system_error> + +#ifndef LLD_READER_WRITER_MACHO_NORMALIZED_FILE_BINARY_UTILS_H +#define LLD_READER_WRITER_MACHO_NORMALIZED_FILE_BINARY_UTILS_H + +namespace lld { +namespace mach_o { +namespace normalized { + +using namespace llvm::support::endian; +using llvm::sys::getSwappedBytes; + +template<typename T> +static inline uint16_t read16(const T *loc, bool isBig) { + assert((uint64_t)loc % llvm::alignOf<T>() == 0 && + "invalid pointer alignment"); + return isBig ? read16be(loc) : read16le(loc); +} + +template<typename T> +static inline uint32_t read32(const T *loc, bool isBig) { + assert((uint64_t)loc % llvm::alignOf<T>() == 0 && + "invalid pointer alignment"); + return isBig ? read32be(loc) : read32le(loc); +} + +template<typename T> +static inline uint64_t read64(const T *loc, bool isBig) { + assert((uint64_t)loc % llvm::alignOf<T>() == 0 && + "invalid pointer alignment"); + return isBig ? read64be(loc) : read64le(loc); +} + +inline void write16(uint8_t *loc, uint16_t value, bool isBig) { + if (isBig) + write16be(loc, value); + else + write16le(loc, value); +} + +inline void write32(uint8_t *loc, uint32_t value, bool isBig) { + if (isBig) + write32be(loc, value); + else + write32le(loc, value); +} + +inline void write64(uint8_t *loc, uint64_t value, bool isBig) { + if (isBig) + write64be(loc, value); + else + write64le(loc, value); +} + +inline uint32_t +bitFieldExtract(uint32_t value, bool isBigEndianBigField, uint8_t firstBit, + uint8_t bitCount) { + const uint32_t mask = ((1<<bitCount)-1); + const uint8_t shift = isBigEndianBigField ? (32-firstBit-bitCount) : firstBit; + return (value >> shift) & mask; +} + +inline void +bitFieldSet(uint32_t &bits, bool isBigEndianBigField, uint32_t newBits, + uint8_t firstBit, uint8_t bitCount) { + const uint32_t mask = ((1<<bitCount)-1); + assert((newBits & mask) == newBits); + const uint8_t shift = isBigEndianBigField ? (32-firstBit-bitCount) : firstBit; + bits &= ~(mask << shift); + bits |= (newBits << shift); +} + +inline Relocation unpackRelocation(const llvm::MachO::any_relocation_info &r, + bool isBigEndian) { + uint32_t r0 = read32(&r.r_word0, isBigEndian); + uint32_t r1 = read32(&r.r_word1, isBigEndian); + + Relocation result; + if (r0 & llvm::MachO::R_SCATTERED) { + // scattered relocation record always laid out like big endian bit field + result.offset = bitFieldExtract(r0, true, 8, 24); + result.scattered = true; + result.type = (RelocationInfoType) + bitFieldExtract(r0, true, 4, 4); + result.length = bitFieldExtract(r0, true, 2, 2); + result.pcRel = bitFieldExtract(r0, true, 1, 1); + result.isExtern = false; + result.value = r1; + result.symbol = 0; + } else { + result.offset = r0; + result.scattered = false; + result.type = (RelocationInfoType) + bitFieldExtract(r1, isBigEndian, 28, 4); + result.length = bitFieldExtract(r1, isBigEndian, 25, 2); + result.pcRel = bitFieldExtract(r1, isBigEndian, 24, 1); + result.isExtern = bitFieldExtract(r1, isBigEndian, 27, 1); + result.value = 0; + result.symbol = bitFieldExtract(r1, isBigEndian, 0, 24); + } + return result; +} + + +inline llvm::MachO::any_relocation_info +packRelocation(const Relocation &r, bool swap, bool isBigEndian) { + uint32_t r0 = 0; + uint32_t r1 = 0; + + if (r.scattered) { + r1 = r.value; + bitFieldSet(r0, true, r.offset, 8, 24); + bitFieldSet(r0, true, r.type, 4, 4); + bitFieldSet(r0, true, r.length, 2, 2); + bitFieldSet(r0, true, r.pcRel, 1, 1); + bitFieldSet(r0, true, r.scattered, 0, 1); // R_SCATTERED + } else { + r0 = r.offset; + bitFieldSet(r1, isBigEndian, r.type, 28, 4); + bitFieldSet(r1, isBigEndian, r.isExtern, 27, 1); + bitFieldSet(r1, isBigEndian, r.length, 25, 2); + bitFieldSet(r1, isBigEndian, r.pcRel, 24, 1); + bitFieldSet(r1, isBigEndian, r.symbol, 0, 24); + } + + llvm::MachO::any_relocation_info result; + result.r_word0 = swap ? getSwappedBytes(r0) : r0; + result.r_word1 = swap ? getSwappedBytes(r1) : r1; + return result; +} + +inline StringRef getString16(const char s[16]) { + StringRef x = s; + if ( x.size() > 16 ) + return x.substr(0, 16); + else + return x; +} + +inline void setString16(StringRef str, char s[16]) { + memset(s, 0, 16); + memcpy(s, str.begin(), (str.size() > 16) ? 16: str.size()); +} + +// Implemented in normalizedToAtoms() and used by normalizedFromAtoms() so +// that the same table can be used to map mach-o sections to and from +// DefinedAtom::ContentType. +void relocatableSectionInfoForContentType(DefinedAtom::ContentType atomType, + StringRef &segmentName, + StringRef §ionName, + SectionType §ionType, + SectionAttr §ionAttrs); + +} // namespace normalized +} // namespace mach_o +} // namespace lld + +#endif // LLD_READER_WRITER_MACHO_NORMALIZED_FILE_BINARY_UTILS_H diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp new file mode 100644 index 0000000000000..be7acf9d4d604 --- /dev/null +++ b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp @@ -0,0 +1,1346 @@ +//===- lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp ---------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +/// +/// \file For mach-o object files, this implementation converts normalized +/// mach-o in memory to mach-o binary on disk. +/// +/// +---------------+ +/// | binary mach-o | +/// +---------------+ +/// ^ +/// | +/// | +/// +------------+ +/// | normalized | +/// +------------+ + +#include "MachONormalizedFile.h" +#include "MachONormalizedFileBinaryUtils.h" +#include "lld/Core/Error.h" +#include "lld/Core/LLVM.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/MachO.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include <functional> +#include <list> +#include <map> +#include <system_error> + +using namespace llvm::MachO; + +namespace lld { +namespace mach_o { +namespace normalized { + +/// Utility class for writing a mach-o binary file given an in-memory +/// normalized file. +class MachOFileLayout { +public: + /// All layout computation is done in the constructor. + MachOFileLayout(const NormalizedFile &file); + + /// Returns the final file size as computed in the constructor. + size_t size() const; + + // Returns size of the mach_header and load commands. + size_t headerAndLoadCommandsSize() const; + + /// Writes the normalized file as a binary mach-o file to the specified + /// path. This does not have a stream interface because the generated + /// file may need the 'x' bit set. + std::error_code writeBinary(StringRef path); + +private: + uint32_t loadCommandsSize(uint32_t &count); + void buildFileOffsets(); + void writeMachHeader(); + std::error_code writeLoadCommands(); + void writeSectionContent(); + void writeRelocations(); + void writeSymbolTable(); + void writeRebaseInfo(); + void writeBindingInfo(); + void writeLazyBindingInfo(); + void writeExportInfo(); + void writeDataInCodeInfo(); + void writeLinkEditContent(); + void buildLinkEditInfo(); + void buildRebaseInfo(); + void buildBindInfo(); + void buildLazyBindInfo(); + void buildExportTrie(); + void computeDataInCodeSize(); + void computeSymbolTableSizes(); + void buildSectionRelocations(); + void appendSymbols(const std::vector<Symbol> &symbols, + uint32_t &symOffset, uint32_t &strOffset); + uint32_t indirectSymbolIndex(const Section §, uint32_t &index); + uint32_t indirectSymbolElementSize(const Section §); + + // For use as template parameter to load command methods. + struct MachO64Trait { + typedef llvm::MachO::segment_command_64 command; + typedef llvm::MachO::section_64 section; + enum { LC = llvm::MachO::LC_SEGMENT_64 }; + }; + + // For use as template parameter to load command methods. + struct MachO32Trait { + typedef llvm::MachO::segment_command command; + typedef llvm::MachO::section section; + enum { LC = llvm::MachO::LC_SEGMENT }; + }; + + template <typename T> + std::error_code writeSingleSegmentLoadCommand(uint8_t *&lc); + template <typename T> std::error_code writeSegmentLoadCommands(uint8_t *&lc); + + uint32_t pointerAlign(uint32_t value); + static StringRef dyldPath(); + + class ByteBuffer { + public: + ByteBuffer() : _ostream(_bytes) { } + + void append_byte(uint8_t b) { + _ostream << b; + } + void append_uleb128(uint64_t value) { + llvm::encodeULEB128(value, _ostream); + } + void append_uleb128Fixed(uint64_t value, unsigned byteCount) { + unsigned min = llvm::getULEB128Size(value); + assert(min <= byteCount); + unsigned pad = byteCount - min; + llvm::encodeULEB128(value, _ostream, pad); + } + void append_sleb128(int64_t value) { + llvm::encodeSLEB128(value, _ostream); + } + void append_string(StringRef str) { + _ostream << str; + append_byte(0); + } + void align(unsigned alignment) { + while ( (_ostream.tell() % alignment) != 0 ) + append_byte(0); + } + size_t size() { + return _ostream.tell(); + } + const uint8_t *bytes() { + return reinterpret_cast<const uint8_t*>(_ostream.str().data()); + } + private: + SmallVector<char, 128> _bytes; + // Stream ivar must be after SmallVector ivar to construct properly. + llvm::raw_svector_ostream _ostream; + }; + + struct TrieNode; // Forward declaration. + + struct TrieEdge { + TrieEdge(StringRef s, TrieNode *node) : _subString(s), _child(node) {} + ~TrieEdge() {} + + StringRef _subString; + struct TrieNode *_child; + }; + + struct TrieNode { + TrieNode(StringRef s) + : _cummulativeString(s), _address(0), _flags(0), _other(0), + _trieOffset(0), _hasExportInfo(false) {} + ~TrieNode() {} + + void addSymbol(const Export &entry, BumpPtrAllocator &allocator, + std::vector<TrieNode *> &allNodes); + bool updateOffset(uint32_t &offset); + void appendToByteBuffer(ByteBuffer &out); + +private: + StringRef _cummulativeString; + std::list<TrieEdge> _children; + uint64_t _address; + uint64_t _flags; + uint64_t _other; + StringRef _importedName; + uint32_t _trieOffset; + bool _hasExportInfo; + }; + + struct SegExtraInfo { + uint32_t fileOffset; + uint32_t fileSize; + std::vector<const Section*> sections; + }; + typedef std::map<const Segment*, SegExtraInfo> SegMap; + struct SectionExtraInfo { + uint32_t fileOffset; + }; + typedef std::map<const Section*, SectionExtraInfo> SectionMap; + + const NormalizedFile &_file; + std::error_code _ec; + uint8_t *_buffer; + const bool _is64; + const bool _swap; + const bool _bigEndianArch; + uint64_t _seg1addr; + uint32_t _startOfLoadCommands; + uint32_t _countOfLoadCommands; + uint32_t _endOfLoadCommands; + uint32_t _startOfRelocations; + uint32_t _startOfDataInCode; + uint32_t _startOfSymbols; + uint32_t _startOfIndirectSymbols; + uint32_t _startOfSymbolStrings; + uint32_t _endOfSymbolStrings; + uint32_t _symbolTableLocalsStartIndex; + uint32_t _symbolTableGlobalsStartIndex; + uint32_t _symbolTableUndefinesStartIndex; + uint32_t _symbolStringPoolSize; + uint32_t _symbolTableSize; + uint32_t _dataInCodeSize; + uint32_t _indirectSymbolTableCount; + // Used in object file creation only + uint32_t _startOfSectionsContent; + uint32_t _endOfSectionsContent; + // Used in final linked image only + uint32_t _startOfLinkEdit; + uint32_t _startOfRebaseInfo; + uint32_t _endOfRebaseInfo; + uint32_t _startOfBindingInfo; + uint32_t _endOfBindingInfo; + uint32_t _startOfLazyBindingInfo; + uint32_t _endOfLazyBindingInfo; + uint32_t _startOfExportTrie; + uint32_t _endOfExportTrie; + uint32_t _endOfLinkEdit; + uint64_t _addressOfLinkEdit; + SegMap _segInfo; + SectionMap _sectInfo; + ByteBuffer _rebaseInfo; + ByteBuffer _bindingInfo; + ByteBuffer _lazyBindingInfo; + ByteBuffer _weakBindingInfo; + ByteBuffer _exportTrie; +}; + +size_t headerAndLoadCommandsSize(const NormalizedFile &file) { + MachOFileLayout layout(file); + return layout.headerAndLoadCommandsSize(); +} + +StringRef MachOFileLayout::dyldPath() { + return "/usr/lib/dyld"; +} + +uint32_t MachOFileLayout::pointerAlign(uint32_t value) { + return llvm::RoundUpToAlignment(value, _is64 ? 8 : 4); +} + + +size_t MachOFileLayout::headerAndLoadCommandsSize() const { + return _endOfLoadCommands; +} + + +MachOFileLayout::MachOFileLayout(const NormalizedFile &file) + : _file(file), + _is64(MachOLinkingContext::is64Bit(file.arch)), + _swap(!MachOLinkingContext::isHostEndian(file.arch)), + _bigEndianArch(MachOLinkingContext::isBigEndian(file.arch)), + _seg1addr(INT64_MAX) { + _startOfLoadCommands = _is64 ? sizeof(mach_header_64) : sizeof(mach_header); + const size_t segCommandBaseSize = + (_is64 ? sizeof(segment_command_64) : sizeof(segment_command)); + const size_t sectsSize = (_is64 ? sizeof(section_64) : sizeof(section)); + if (file.fileType == llvm::MachO::MH_OBJECT) { + // object files have just one segment load command containing all sections + _endOfLoadCommands = _startOfLoadCommands + + segCommandBaseSize + + file.sections.size() * sectsSize + + sizeof(symtab_command); + _countOfLoadCommands = 2; + if (!_file.dataInCode.empty()) { + _endOfLoadCommands += sizeof(linkedit_data_command); + _countOfLoadCommands++; + } + // Assign file offsets to each section. + _startOfSectionsContent = _endOfLoadCommands; + unsigned relocCount = 0; + uint64_t offset = _startOfSectionsContent; + for (const Section § : file.sections) { + if (sect.type != llvm::MachO::S_ZEROFILL) { + offset = llvm::RoundUpToAlignment(offset, 1 << sect.alignment); + _sectInfo[§].fileOffset = offset; + offset += sect.content.size(); + } else { + _sectInfo[§].fileOffset = 0; + } + relocCount += sect.relocations.size(); + } + _endOfSectionsContent = offset; + + computeSymbolTableSizes(); + computeDataInCodeSize(); + + // Align start of relocations. + _startOfRelocations = pointerAlign(_endOfSectionsContent); + _startOfDataInCode = _startOfRelocations + relocCount * 8; + _startOfSymbols = _startOfDataInCode + _dataInCodeSize; + // Add Indirect symbol table. + _startOfIndirectSymbols = _startOfSymbols + _symbolTableSize; + // Align start of symbol table and symbol strings. + _startOfSymbolStrings = _startOfIndirectSymbols + + pointerAlign(_indirectSymbolTableCount * sizeof(uint32_t)); + _endOfSymbolStrings = _startOfSymbolStrings + + pointerAlign(_symbolStringPoolSize); + _endOfLinkEdit = _endOfSymbolStrings; + DEBUG_WITH_TYPE("MachOFileLayout", + llvm::dbgs() << "MachOFileLayout()\n" + << " startOfLoadCommands=" << _startOfLoadCommands << "\n" + << " countOfLoadCommands=" << _countOfLoadCommands << "\n" + << " endOfLoadCommands=" << _endOfLoadCommands << "\n" + << " startOfRelocations=" << _startOfRelocations << "\n" + << " startOfSymbols=" << _startOfSymbols << "\n" + << " startOfSymbolStrings=" << _startOfSymbolStrings << "\n" + << " endOfSymbolStrings=" << _endOfSymbolStrings << "\n" + << " startOfSectionsContent=" << _startOfSectionsContent << "\n" + << " endOfSectionsContent=" << _endOfSectionsContent << "\n"); + } else { + // Final linked images have one load command per segment. + _endOfLoadCommands = _startOfLoadCommands + + loadCommandsSize(_countOfLoadCommands); + + // Assign section file offsets. + buildFileOffsets(); + buildLinkEditInfo(); + + // LINKEDIT of final linked images has in order: + // rebase info, binding info, lazy binding info, weak binding info, + // data-in-code, symbol table, indirect symbol table, symbol table strings. + _startOfRebaseInfo = _startOfLinkEdit; + _endOfRebaseInfo = _startOfRebaseInfo + _rebaseInfo.size(); + _startOfBindingInfo = _endOfRebaseInfo; + _endOfBindingInfo = _startOfBindingInfo + _bindingInfo.size(); + _startOfLazyBindingInfo = _endOfBindingInfo; + _endOfLazyBindingInfo = _startOfLazyBindingInfo + _lazyBindingInfo.size(); + _startOfExportTrie = _endOfLazyBindingInfo; + _endOfExportTrie = _startOfExportTrie + _exportTrie.size(); + _startOfDataInCode = _endOfExportTrie; + _startOfSymbols = _startOfDataInCode + _dataInCodeSize; + _startOfIndirectSymbols = _startOfSymbols + _symbolTableSize; + _startOfSymbolStrings = _startOfIndirectSymbols + + pointerAlign(_indirectSymbolTableCount * sizeof(uint32_t)); + _endOfSymbolStrings = _startOfSymbolStrings + + pointerAlign(_symbolStringPoolSize); + _endOfLinkEdit = _endOfSymbolStrings; + DEBUG_WITH_TYPE("MachOFileLayout", + llvm::dbgs() << "MachOFileLayout()\n" + << " startOfLoadCommands=" << _startOfLoadCommands << "\n" + << " countOfLoadCommands=" << _countOfLoadCommands << "\n" + << " endOfLoadCommands=" << _endOfLoadCommands << "\n" + << " startOfLinkEdit=" << _startOfLinkEdit << "\n" + << " startOfRebaseInfo=" << _startOfRebaseInfo << "\n" + << " endOfRebaseInfo=" << _endOfRebaseInfo << "\n" + << " startOfBindingInfo=" << _startOfBindingInfo << "\n" + << " endOfBindingInfo=" << _endOfBindingInfo << "\n" + << " startOfLazyBindingInfo=" << _startOfLazyBindingInfo << "\n" + << " endOfLazyBindingInfo=" << _endOfLazyBindingInfo << "\n" + << " startOfExportTrie=" << _startOfExportTrie << "\n" + << " endOfExportTrie=" << _endOfExportTrie << "\n" + << " startOfDataInCode=" << _startOfDataInCode << "\n" + << " startOfSymbols=" << _startOfSymbols << "\n" + << " startOfSymbolStrings=" << _startOfSymbolStrings << "\n" + << " endOfSymbolStrings=" << _endOfSymbolStrings << "\n" + << " addressOfLinkEdit=" << _addressOfLinkEdit << "\n"); + } +} + +uint32_t MachOFileLayout::loadCommandsSize(uint32_t &count) { + uint32_t size = 0; + count = 0; + + const size_t segCommandSize = + (_is64 ? sizeof(segment_command_64) : sizeof(segment_command)); + const size_t sectionSize = (_is64 ? sizeof(section_64) : sizeof(section)); + + // Add LC_SEGMENT for each segment. + size += _file.segments.size() * segCommandSize; + count += _file.segments.size(); + // Add section record for each section. + size += _file.sections.size() * sectionSize; + // Add one LC_SEGMENT for implicit __LINKEDIT segment + size += segCommandSize; + ++count; + + // If creating a dylib, add LC_ID_DYLIB. + if (_file.fileType == llvm::MachO::MH_DYLIB) { + size += sizeof(dylib_command) + pointerAlign(_file.installName.size() + 1); + ++count; + } + + // Add LC_DYLD_INFO + size += sizeof(dyld_info_command); + ++count; + + // Add LC_SYMTAB + size += sizeof(symtab_command); + ++count; + + // Add LC_DYSYMTAB + if (_file.fileType != llvm::MachO::MH_PRELOAD) { + size += sizeof(dysymtab_command); + ++count; + } + + // If main executable add LC_LOAD_DYLINKER and LC_MAIN + if (_file.fileType == llvm::MachO::MH_EXECUTE) { + size += pointerAlign(sizeof(dylinker_command) + dyldPath().size()+1); + ++count; + size += sizeof(entry_point_command); + ++count; + } + + // Add LC_LOAD_DYLIB for each dependent dylib. + for (const DependentDylib &dep : _file.dependentDylibs) { + size += sizeof(dylib_command) + pointerAlign(dep.path.size()+1); + ++count; + } + + // Add LC_RPATH + for (const StringRef &path : _file.rpaths) { + size += sizeof(rpath_command) + pointerAlign(path.size()+1); + ++count; + } + + // Add LC_DATA_IN_CODE if needed + if (!_file.dataInCode.empty()) { + size += sizeof(linkedit_data_command); + ++count; + } + + return size; +} + +static bool overlaps(const Segment &s1, const Segment &s2) { + if (s2.address >= s1.address+s1.size) + return false; + if (s1.address >= s2.address+s2.size) + return false; + return true; +} + +static bool overlaps(const Section &s1, const Section &s2) { + if (s2.address >= s1.address+s1.content.size()) + return false; + if (s1.address >= s2.address+s2.content.size()) + return false; + return true; +} + +void MachOFileLayout::buildFileOffsets() { + // Verify no segments overlap + for (const Segment &sg1 : _file.segments) { + for (const Segment &sg2 : _file.segments) { + if (&sg1 == &sg2) + continue; + if (overlaps(sg1,sg2)) { + _ec = make_error_code(llvm::errc::executable_format_error); + return; + } + } + } + + // Verify no sections overlap + for (const Section &s1 : _file.sections) { + for (const Section &s2 : _file.sections) { + if (&s1 == &s2) + continue; + if (overlaps(s1,s2)) { + _ec = make_error_code(llvm::errc::executable_format_error); + return; + } + } + } + + // Build side table of extra info about segments and sections. + SegExtraInfo t; + t.fileOffset = 0; + for (const Segment &sg : _file.segments) { + _segInfo[&sg] = t; + } + SectionExtraInfo t2; + t2.fileOffset = 0; + // Assign sections to segments. + for (const Section &s : _file.sections) { + _sectInfo[&s] = t2; + bool foundSegment = false; + for (const Segment &sg : _file.segments) { + if (sg.name.equals(s.segmentName)) { + if ((s.address >= sg.address) + && (s.address+s.content.size() <= sg.address+sg.size)) { + _segInfo[&sg].sections.push_back(&s); + foundSegment = true; + break; + } + } + } + if (!foundSegment) { + _ec = make_error_code(llvm::errc::executable_format_error); + return; + } + } + + // Assign file offsets. + uint32_t fileOffset = 0; + DEBUG_WITH_TYPE("MachOFileLayout", + llvm::dbgs() << "buildFileOffsets()\n"); + for (const Segment &sg : _file.segments) { + _segInfo[&sg].fileOffset = fileOffset; + if ((_seg1addr == INT64_MAX) && sg.access) + _seg1addr = sg.address; + DEBUG_WITH_TYPE("MachOFileLayout", + llvm::dbgs() << " segment=" << sg.name + << ", fileOffset=" << _segInfo[&sg].fileOffset << "\n"); + + uint32_t segFileSize = 0; + // A segment that is not zero-fill must use a least one page of disk space. + if (sg.access) + segFileSize = _file.pageSize; + for (const Section *s : _segInfo[&sg].sections) { + uint32_t sectOffset = s->address - sg.address; + uint32_t sectFileSize = + s->type == llvm::MachO::S_ZEROFILL ? 0 : s->content.size(); + segFileSize = std::max(segFileSize, sectOffset + sectFileSize); + + _sectInfo[s].fileOffset = _segInfo[&sg].fileOffset + sectOffset; + DEBUG_WITH_TYPE("MachOFileLayout", + llvm::dbgs() << " section=" << s->sectionName + << ", fileOffset=" << fileOffset << "\n"); + } + + _segInfo[&sg].fileSize = llvm::RoundUpToAlignment(segFileSize, + _file.pageSize); + fileOffset = llvm::RoundUpToAlignment(fileOffset + segFileSize, + _file.pageSize); + _addressOfLinkEdit = sg.address + sg.size; + } + _startOfLinkEdit = fileOffset; +} + + +size_t MachOFileLayout::size() const { + return _endOfSymbolStrings; +} + +void MachOFileLayout::writeMachHeader() { + mach_header *mh = reinterpret_cast<mach_header*>(_buffer); + mh->magic = _is64 ? llvm::MachO::MH_MAGIC_64 : llvm::MachO::MH_MAGIC; + mh->cputype = MachOLinkingContext::cpuTypeFromArch(_file.arch); + mh->cpusubtype = MachOLinkingContext::cpuSubtypeFromArch(_file.arch); + mh->filetype = _file.fileType; + mh->ncmds = _countOfLoadCommands; + mh->sizeofcmds = _endOfLoadCommands - _startOfLoadCommands; + mh->flags = _file.flags; + if (_swap) + swapStruct(*mh); +} + +uint32_t MachOFileLayout::indirectSymbolIndex(const Section §, + uint32_t &index) { + if (sect.indirectSymbols.empty()) + return 0; + uint32_t result = index; + index += sect.indirectSymbols.size(); + return result; +} + +uint32_t MachOFileLayout::indirectSymbolElementSize(const Section §) { + if (sect.indirectSymbols.empty()) + return 0; + if (sect.type != S_SYMBOL_STUBS) + return 0; + return sect.content.size() / sect.indirectSymbols.size(); +} + +template <typename T> +std::error_code MachOFileLayout::writeSingleSegmentLoadCommand(uint8_t *&lc) { + typename T::command* seg = reinterpret_cast<typename T::command*>(lc); + seg->cmd = T::LC; + seg->cmdsize = sizeof(typename T::command) + + _file.sections.size() * sizeof(typename T::section); + uint8_t *next = lc + seg->cmdsize; + memset(seg->segname, 0, 16); + seg->vmaddr = 0; + seg->vmsize = _file.sections.back().address + + _file.sections.back().content.size(); + seg->fileoff = _endOfLoadCommands; + seg->filesize = seg->vmsize; + seg->maxprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE; + seg->initprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE; + seg->nsects = _file.sections.size(); + seg->flags = 0; + if (_swap) + swapStruct(*seg); + typename T::section *sout = reinterpret_cast<typename T::section*> + (lc+sizeof(typename T::command)); + uint32_t relOffset = _startOfRelocations; + uint32_t indirectSymRunningIndex = 0; + for (const Section &sin : _file.sections) { + setString16(sin.sectionName, sout->sectname); + setString16(sin.segmentName, sout->segname); + sout->addr = sin.address; + sout->size = sin.content.size(); + sout->offset = _sectInfo[&sin].fileOffset; + sout->align = sin.alignment; + sout->reloff = sin.relocations.empty() ? 0 : relOffset; + sout->nreloc = sin.relocations.size(); + sout->flags = sin.type | sin.attributes; + sout->reserved1 = indirectSymbolIndex(sin, indirectSymRunningIndex); + sout->reserved2 = indirectSymbolElementSize(sin); + relOffset += sin.relocations.size() * sizeof(any_relocation_info); + if (_swap) + swapStruct(*sout); + ++sout; + } + lc = next; + return std::error_code(); +} + +template <typename T> +std::error_code MachOFileLayout::writeSegmentLoadCommands(uint8_t *&lc) { + uint32_t indirectSymRunningIndex = 0; + for (const Segment &seg : _file.segments) { + // Write segment command with trailing sections. + SegExtraInfo &segInfo = _segInfo[&seg]; + typename T::command* cmd = reinterpret_cast<typename T::command*>(lc); + cmd->cmd = T::LC; + cmd->cmdsize = sizeof(typename T::command) + + segInfo.sections.size() * sizeof(typename T::section); + uint8_t *next = lc + cmd->cmdsize; + setString16(seg.name, cmd->segname); + cmd->vmaddr = seg.address; + cmd->vmsize = seg.size; + cmd->fileoff = segInfo.fileOffset; + cmd->filesize = segInfo.fileSize; + cmd->maxprot = seg.access; + cmd->initprot = seg.access; + cmd->nsects = segInfo.sections.size(); + cmd->flags = 0; + if (_swap) + swapStruct(*cmd); + typename T::section *sect = reinterpret_cast<typename T::section*> + (lc+sizeof(typename T::command)); + for (const Section *section : segInfo.sections) { + setString16(section->sectionName, sect->sectname); + setString16(section->segmentName, sect->segname); + sect->addr = section->address; + sect->size = section->content.size(); + if (section->type == llvm::MachO::S_ZEROFILL) + sect->offset = 0; + else + sect->offset = section->address - seg.address + segInfo.fileOffset; + sect->align = section->alignment; + sect->reloff = 0; + sect->nreloc = 0; + sect->flags = section->type | section->attributes; + sect->reserved1 = indirectSymbolIndex(*section, indirectSymRunningIndex); + sect->reserved2 = indirectSymbolElementSize(*section); + if (_swap) + swapStruct(*sect); + ++sect; + } + lc = reinterpret_cast<uint8_t*>(next); + } + // Add implicit __LINKEDIT segment + size_t linkeditSize = _endOfLinkEdit - _startOfLinkEdit; + typename T::command* cmd = reinterpret_cast<typename T::command*>(lc); + cmd->cmd = T::LC; + cmd->cmdsize = sizeof(typename T::command); + uint8_t *next = lc + cmd->cmdsize; + setString16("__LINKEDIT", cmd->segname); + cmd->vmaddr = _addressOfLinkEdit; + cmd->vmsize = llvm::RoundUpToAlignment(linkeditSize, _file.pageSize); + cmd->fileoff = _startOfLinkEdit; + cmd->filesize = linkeditSize; + cmd->maxprot = VM_PROT_READ; + cmd->initprot = VM_PROT_READ; + cmd->nsects = 0; + cmd->flags = 0; + if (_swap) + swapStruct(*cmd); + lc = next; + return std::error_code(); +} + +std::error_code MachOFileLayout::writeLoadCommands() { + std::error_code ec; + uint8_t *lc = &_buffer[_startOfLoadCommands]; + if (_file.fileType == llvm::MachO::MH_OBJECT) { + // Object files have one unnamed segment which holds all sections. + if (_is64) + ec = writeSingleSegmentLoadCommand<MachO64Trait>(lc); + else + ec = writeSingleSegmentLoadCommand<MachO32Trait>(lc); + // Add LC_SYMTAB with symbol table info + symtab_command* st = reinterpret_cast<symtab_command*>(lc); + st->cmd = LC_SYMTAB; + st->cmdsize = sizeof(symtab_command); + st->symoff = _startOfSymbols; + st->nsyms = _file.localSymbols.size() + _file.globalSymbols.size() + + _file.undefinedSymbols.size(); + st->stroff = _startOfSymbolStrings; + st->strsize = _endOfSymbolStrings - _startOfSymbolStrings; + if (_swap) + swapStruct(*st); + lc += sizeof(symtab_command); + // Add LC_DATA_IN_CODE if needed. + if (_dataInCodeSize != 0) { + linkedit_data_command* dl = reinterpret_cast<linkedit_data_command*>(lc); + dl->cmd = LC_DATA_IN_CODE; + dl->cmdsize = sizeof(linkedit_data_command); + dl->dataoff = _startOfDataInCode; + dl->datasize = _dataInCodeSize; + if (_swap) + swapStruct(*dl); + lc += sizeof(linkedit_data_command); + } + } else { + // Final linked images have sections under segments. + if (_is64) + ec = writeSegmentLoadCommands<MachO64Trait>(lc); + else + ec = writeSegmentLoadCommands<MachO32Trait>(lc); + + // Add LC_ID_DYLIB command for dynamic libraries. + if (_file.fileType == llvm::MachO::MH_DYLIB) { + dylib_command *dc = reinterpret_cast<dylib_command*>(lc); + StringRef path = _file.installName; + uint32_t size = sizeof(dylib_command) + pointerAlign(path.size() + 1); + dc->cmd = LC_ID_DYLIB; + dc->cmdsize = size; + dc->dylib.name = sizeof(dylib_command); // offset + // needs to be some constant value different than the one in LC_LOAD_DYLIB + dc->dylib.timestamp = 1; + dc->dylib.current_version = _file.currentVersion; + dc->dylib.compatibility_version = _file.compatVersion; + if (_swap) + swapStruct(*dc); + memcpy(lc + sizeof(dylib_command), path.begin(), path.size()); + lc[sizeof(dylib_command) + path.size()] = '\0'; + lc += size; + } + + // Add LC_DYLD_INFO_ONLY. + dyld_info_command* di = reinterpret_cast<dyld_info_command*>(lc); + di->cmd = LC_DYLD_INFO_ONLY; + di->cmdsize = sizeof(dyld_info_command); + di->rebase_off = _rebaseInfo.size() ? _startOfRebaseInfo : 0; + di->rebase_size = _rebaseInfo.size(); + di->bind_off = _bindingInfo.size() ? _startOfBindingInfo : 0; + di->bind_size = _bindingInfo.size(); + di->weak_bind_off = 0; + di->weak_bind_size = 0; + di->lazy_bind_off = _lazyBindingInfo.size() ? _startOfLazyBindingInfo : 0; + di->lazy_bind_size = _lazyBindingInfo.size(); + di->export_off = _exportTrie.size() ? _startOfExportTrie : 0; + di->export_size = _exportTrie.size(); + if (_swap) + swapStruct(*di); + lc += sizeof(dyld_info_command); + + // Add LC_SYMTAB with symbol table info. + symtab_command* st = reinterpret_cast<symtab_command*>(lc); + st->cmd = LC_SYMTAB; + st->cmdsize = sizeof(symtab_command); + st->symoff = _startOfSymbols; + st->nsyms = _file.localSymbols.size() + _file.globalSymbols.size() + + _file.undefinedSymbols.size(); + st->stroff = _startOfSymbolStrings; + st->strsize = _endOfSymbolStrings - _startOfSymbolStrings; + if (_swap) + swapStruct(*st); + lc += sizeof(symtab_command); + + // Add LC_DYSYMTAB + if (_file.fileType != llvm::MachO::MH_PRELOAD) { + dysymtab_command* dst = reinterpret_cast<dysymtab_command*>(lc); + dst->cmd = LC_DYSYMTAB; + dst->cmdsize = sizeof(dysymtab_command); + dst->ilocalsym = _symbolTableLocalsStartIndex; + dst->nlocalsym = _file.localSymbols.size(); + dst->iextdefsym = _symbolTableGlobalsStartIndex; + dst->nextdefsym = _file.globalSymbols.size(); + dst->iundefsym = _symbolTableUndefinesStartIndex; + dst->nundefsym = _file.undefinedSymbols.size(); + dst->tocoff = 0; + dst->ntoc = 0; + dst->modtaboff = 0; + dst->nmodtab = 0; + dst->extrefsymoff = 0; + dst->nextrefsyms = 0; + dst->indirectsymoff = _startOfIndirectSymbols; + dst->nindirectsyms = _indirectSymbolTableCount; + dst->extreloff = 0; + dst->nextrel = 0; + dst->locreloff = 0; + dst->nlocrel = 0; + if (_swap) + swapStruct(*dst); + lc += sizeof(dysymtab_command); + } + + // If main executable, add LC_LOAD_DYLINKER and LC_MAIN. + if (_file.fileType == llvm::MachO::MH_EXECUTE) { + // Build LC_LOAD_DYLINKER load command. + uint32_t size=pointerAlign(sizeof(dylinker_command)+dyldPath().size()+1); + dylinker_command* dl = reinterpret_cast<dylinker_command*>(lc); + dl->cmd = LC_LOAD_DYLINKER; + dl->cmdsize = size; + dl->name = sizeof(dylinker_command); // offset + if (_swap) + swapStruct(*dl); + memcpy(lc+sizeof(dylinker_command), dyldPath().data(), dyldPath().size()); + lc[sizeof(dylinker_command)+dyldPath().size()] = '\0'; + lc += size; + // Build LC_MAIN load command. + entry_point_command* ep = reinterpret_cast<entry_point_command*>(lc); + ep->cmd = LC_MAIN; + ep->cmdsize = sizeof(entry_point_command); + ep->entryoff = _file.entryAddress - _seg1addr; + ep->stacksize = 0; + if (_swap) + swapStruct(*ep); + lc += sizeof(entry_point_command); + } + + // Add LC_LOAD_DYLIB commands + for (const DependentDylib &dep : _file.dependentDylibs) { + dylib_command* dc = reinterpret_cast<dylib_command*>(lc); + uint32_t size = sizeof(dylib_command) + pointerAlign(dep.path.size()+1); + dc->cmd = dep.kind; + dc->cmdsize = size; + dc->dylib.name = sizeof(dylib_command); // offset + // needs to be some constant value different than the one in LC_ID_DYLIB + dc->dylib.timestamp = 2; + dc->dylib.current_version = dep.currentVersion; + dc->dylib.compatibility_version = dep.compatVersion; + if (_swap) + swapStruct(*dc); + memcpy(lc+sizeof(dylib_command), dep.path.begin(), dep.path.size()); + lc[sizeof(dylib_command)+dep.path.size()] = '\0'; + lc += size; + } + + // Add LC_RPATH + for (const StringRef &path : _file.rpaths) { + rpath_command *rpc = reinterpret_cast<rpath_command *>(lc); + uint32_t size = sizeof(rpath_command) + pointerAlign(path.size()+1); + rpc->cmd = LC_RPATH; + rpc->cmdsize = size; + rpc->path = sizeof(rpath_command); // offset + if (_swap) + swapStruct(*rpc); + memcpy(lc+sizeof(rpath_command), path.begin(), path.size()); + lc[sizeof(rpath_command)+path.size()] = '\0'; + lc += size; + } + + // Add LC_DATA_IN_CODE if needed. + if (_dataInCodeSize != 0) { + linkedit_data_command* dl = reinterpret_cast<linkedit_data_command*>(lc); + dl->cmd = LC_DATA_IN_CODE; + dl->cmdsize = sizeof(linkedit_data_command); + dl->dataoff = _startOfDataInCode; + dl->datasize = _dataInCodeSize; + if (_swap) + swapStruct(*dl); + lc += sizeof(linkedit_data_command); + } + } + return ec; +} + + +void MachOFileLayout::writeSectionContent() { + for (const Section &s : _file.sections) { + // Copy all section content to output buffer. + if (s.type == llvm::MachO::S_ZEROFILL) + continue; + if (s.content.empty()) + continue; + uint32_t offset = _sectInfo[&s].fileOffset; + uint8_t *p = &_buffer[offset]; + memcpy(p, &s.content[0], s.content.size()); + p += s.content.size(); + } +} + +void MachOFileLayout::writeRelocations() { + uint32_t relOffset = _startOfRelocations; + for (Section sect : _file.sections) { + for (Relocation r : sect.relocations) { + any_relocation_info* rb = reinterpret_cast<any_relocation_info*>( + &_buffer[relOffset]); + *rb = packRelocation(r, _swap, _bigEndianArch); + relOffset += sizeof(any_relocation_info); + } + } +} + + +void MachOFileLayout::appendSymbols(const std::vector<Symbol> &symbols, + uint32_t &symOffset, uint32_t &strOffset) { + for (const Symbol &sym : symbols) { + if (_is64) { + nlist_64* nb = reinterpret_cast<nlist_64*>(&_buffer[symOffset]); + nb->n_strx = strOffset - _startOfSymbolStrings; + nb->n_type = sym.type | sym.scope; + nb->n_sect = sym.sect; + nb->n_desc = sym.desc; + nb->n_value = sym.value; + if (_swap) + swapStruct(*nb); + symOffset += sizeof(nlist_64); + } else { + nlist* nb = reinterpret_cast<nlist*>(&_buffer[symOffset]); + nb->n_strx = strOffset - _startOfSymbolStrings; + nb->n_type = sym.type | sym.scope; + nb->n_sect = sym.sect; + nb->n_desc = sym.desc; + nb->n_value = sym.value; + if (_swap) + swapStruct(*nb); + symOffset += sizeof(nlist); + } + memcpy(&_buffer[strOffset], sym.name.begin(), sym.name.size()); + strOffset += sym.name.size(); + _buffer[strOffset++] ='\0'; // Strings in table have nul terminator. + } +} + +void MachOFileLayout::writeDataInCodeInfo() { + uint32_t offset = _startOfDataInCode; + for (const DataInCode &entry : _file.dataInCode) { + data_in_code_entry *dst = reinterpret_cast<data_in_code_entry*>( + &_buffer[offset]); + dst->offset = entry.offset; + dst->length = entry.length; + dst->kind = entry.kind; + if (_swap) + swapStruct(*dst); + offset += sizeof(data_in_code_entry); + } +} + +void MachOFileLayout::writeSymbolTable() { + // Write symbol table and symbol strings in parallel. + uint32_t symOffset = _startOfSymbols; + uint32_t strOffset = _startOfSymbolStrings; + _buffer[strOffset++] = '\0'; // Reserve n_strx offset of zero to mean no name. + appendSymbols(_file.localSymbols, symOffset, strOffset); + appendSymbols(_file.globalSymbols, symOffset, strOffset); + appendSymbols(_file.undefinedSymbols, symOffset, strOffset); + // Write indirect symbol table array. + uint32_t *indirects = reinterpret_cast<uint32_t*> + (&_buffer[_startOfIndirectSymbols]); + if (_file.fileType == llvm::MachO::MH_OBJECT) { + // Object files have sections in same order as input normalized file. + for (const Section §ion : _file.sections) { + for (uint32_t index : section.indirectSymbols) { + if (_swap) + *indirects++ = llvm::sys::getSwappedBytes(index); + else + *indirects++ = index; + } + } + } else { + // Final linked images must sort sections from normalized file. + for (const Segment &seg : _file.segments) { + SegExtraInfo &segInfo = _segInfo[&seg]; + for (const Section *section : segInfo.sections) { + for (uint32_t index : section->indirectSymbols) { + if (_swap) + *indirects++ = llvm::sys::getSwappedBytes(index); + else + *indirects++ = index; + } + } + } + } +} + +void MachOFileLayout::writeRebaseInfo() { + memcpy(&_buffer[_startOfRebaseInfo], _rebaseInfo.bytes(), _rebaseInfo.size()); +} + +void MachOFileLayout::writeBindingInfo() { + memcpy(&_buffer[_startOfBindingInfo], + _bindingInfo.bytes(), _bindingInfo.size()); +} + +void MachOFileLayout::writeLazyBindingInfo() { + memcpy(&_buffer[_startOfLazyBindingInfo], + _lazyBindingInfo.bytes(), _lazyBindingInfo.size()); +} + +void MachOFileLayout::writeExportInfo() { + memcpy(&_buffer[_startOfExportTrie], _exportTrie.bytes(), _exportTrie.size()); +} + +void MachOFileLayout::buildLinkEditInfo() { + buildRebaseInfo(); + buildBindInfo(); + buildLazyBindInfo(); + buildExportTrie(); + computeSymbolTableSizes(); + computeDataInCodeSize(); +} + +void MachOFileLayout::buildSectionRelocations() { + +} + +void MachOFileLayout::buildRebaseInfo() { + // TODO: compress rebasing info. + for (const RebaseLocation& entry : _file.rebasingInfo) { + _rebaseInfo.append_byte(REBASE_OPCODE_SET_TYPE_IMM | entry.kind); + _rebaseInfo.append_byte(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB + | entry.segIndex); + _rebaseInfo.append_uleb128(entry.segOffset); + _rebaseInfo.append_uleb128(REBASE_OPCODE_DO_REBASE_IMM_TIMES | 1); + } + _rebaseInfo.append_byte(REBASE_OPCODE_DONE); + _rebaseInfo.align(_is64 ? 8 : 4); +} + +void MachOFileLayout::buildBindInfo() { + // TODO: compress bind info. + uint64_t lastAddend = 0; + for (const BindLocation& entry : _file.bindingInfo) { + _bindingInfo.append_byte(BIND_OPCODE_SET_TYPE_IMM | entry.kind); + _bindingInfo.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB + | entry.segIndex); + _bindingInfo.append_uleb128(entry.segOffset); + _bindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | entry.ordinal); + _bindingInfo.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); + _bindingInfo.append_string(entry.symbolName); + if (entry.addend != lastAddend) { + _bindingInfo.append_byte(BIND_OPCODE_SET_ADDEND_SLEB); + _bindingInfo.append_sleb128(entry.addend); + lastAddend = entry.addend; + } + _bindingInfo.append_byte(BIND_OPCODE_DO_BIND); + } + _bindingInfo.append_byte(BIND_OPCODE_DONE); + _bindingInfo.align(_is64 ? 8 : 4); +} + +void MachOFileLayout::buildLazyBindInfo() { + for (const BindLocation& entry : _file.lazyBindingInfo) { + _lazyBindingInfo.append_byte(BIND_OPCODE_SET_TYPE_IMM | entry.kind); + _lazyBindingInfo.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB + | entry.segIndex); + _lazyBindingInfo.append_uleb128Fixed(entry.segOffset, 5); + _lazyBindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | entry.ordinal); + _lazyBindingInfo.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); + _lazyBindingInfo.append_string(entry.symbolName); + _lazyBindingInfo.append_byte(BIND_OPCODE_DO_BIND); + _lazyBindingInfo.append_byte(BIND_OPCODE_DONE); + } + _lazyBindingInfo.append_byte(BIND_OPCODE_DONE); + _lazyBindingInfo.align(_is64 ? 8 : 4); +} + +void MachOFileLayout::TrieNode::addSymbol(const Export& entry, + BumpPtrAllocator &allocator, + std::vector<TrieNode*> &allNodes) { + StringRef partialStr = entry.name.drop_front(_cummulativeString.size()); + for (TrieEdge &edge : _children) { + StringRef edgeStr = edge._subString; + if (partialStr.startswith(edgeStr)) { + // Already have matching edge, go down that path. + edge._child->addSymbol(entry, allocator, allNodes); + return; + } + // See if string has commmon prefix with existing edge. + for (int n=edgeStr.size()-1; n > 0; --n) { + if (partialStr.substr(0, n).equals(edgeStr.substr(0, n))) { + // Splice in new node: was A -> C, now A -> B -> C + StringRef bNodeStr = edge._child->_cummulativeString; + bNodeStr = bNodeStr.drop_back(edgeStr.size()-n).copy(allocator); + TrieNode* bNode = new (allocator) TrieNode(bNodeStr); + allNodes.push_back(bNode); + TrieNode* cNode = edge._child; + StringRef abEdgeStr = edgeStr.substr(0,n).copy(allocator); + StringRef bcEdgeStr = edgeStr.substr(n).copy(allocator); + DEBUG_WITH_TYPE("trie-builder", llvm::dbgs() + << "splice in TrieNode('" << bNodeStr + << "') between edge '" + << abEdgeStr << "' and edge='" + << bcEdgeStr<< "'\n"); + TrieEdge& abEdge = edge; + abEdge._subString = abEdgeStr; + abEdge._child = bNode; + TrieEdge *bcEdge = new (allocator) TrieEdge(bcEdgeStr, cNode); + bNode->_children.push_back(std::move(*bcEdge)); + bNode->addSymbol(entry, allocator, allNodes); + return; + } + } + } + if (entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) { + assert(entry.otherOffset != 0); + } + if (entry.flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) { + assert(entry.otherOffset != 0); + } + // No commonality with any existing child, make a new edge. + TrieNode* newNode = new (allocator) TrieNode(entry.name.copy(allocator)); + TrieEdge *newEdge = new (allocator) TrieEdge(partialStr, newNode); + _children.push_back(std::move(*newEdge)); + DEBUG_WITH_TYPE("trie-builder", llvm::dbgs() + << "new TrieNode('" << entry.name << "') with edge '" + << partialStr << "' from node='" + << _cummulativeString << "'\n"); + newNode->_address = entry.offset; + newNode->_flags = entry.flags | entry.kind; + newNode->_other = entry.otherOffset; + if ((entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) && !entry.otherName.empty()) + newNode->_importedName = entry.otherName.copy(allocator); + newNode->_hasExportInfo = true; + allNodes.push_back(newNode); +} + +bool MachOFileLayout::TrieNode::updateOffset(uint32_t& offset) { + uint32_t nodeSize = 1; // Length when no export info + if (_hasExportInfo) { + if (_flags & EXPORT_SYMBOL_FLAGS_REEXPORT) { + nodeSize = llvm::getULEB128Size(_flags); + nodeSize += llvm::getULEB128Size(_other); // Other contains ordinal. + nodeSize += _importedName.size(); + ++nodeSize; // Trailing zero in imported name. + } else { + nodeSize = llvm::getULEB128Size(_flags) + llvm::getULEB128Size(_address); + if (_flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) + nodeSize += llvm::getULEB128Size(_other); + } + // Overall node size so far is uleb128 of export info + actual export info. + nodeSize += llvm::getULEB128Size(nodeSize); + } + // Compute size of all child edges. + ++nodeSize; // Byte for number of chidren. + for (TrieEdge &edge : _children) { + nodeSize += edge._subString.size() + 1 // String length. + + llvm::getULEB128Size(edge._child->_trieOffset); // Offset len. + } + // On input, 'offset' is new prefered location for this node. + bool result = (_trieOffset != offset); + // Store new location in node object for use by parents. + _trieOffset = offset; + // Update offset for next iteration. + offset += nodeSize; + // Return true if _trieOffset was changed. + return result; +} + +void MachOFileLayout::TrieNode::appendToByteBuffer(ByteBuffer &out) { + if (_hasExportInfo) { + if (_flags & EXPORT_SYMBOL_FLAGS_REEXPORT) { + if (!_importedName.empty()) { + // nodes with re-export info: size, flags, ordinal, import-name + uint32_t nodeSize = llvm::getULEB128Size(_flags) + + llvm::getULEB128Size(_other) + + _importedName.size() + 1; + assert(nodeSize < 256); + out.append_byte(nodeSize); + out.append_uleb128(_flags); + out.append_uleb128(_other); + out.append_string(_importedName); + } else { + // nodes without re-export info: size, flags, ordinal, empty-string + uint32_t nodeSize = llvm::getULEB128Size(_flags) + + llvm::getULEB128Size(_other) + 1; + assert(nodeSize < 256); + out.append_byte(nodeSize); + out.append_uleb128(_flags); + out.append_uleb128(_other); + out.append_byte(0); + } + } else if ( _flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) { + // Nodes with export info: size, flags, address, other + uint32_t nodeSize = llvm::getULEB128Size(_flags) + + llvm::getULEB128Size(_address) + + llvm::getULEB128Size(_other); + assert(nodeSize < 256); + out.append_byte(nodeSize); + out.append_uleb128(_flags); + out.append_uleb128(_address); + out.append_uleb128(_other); + } else { + // Nodes with export info: size, flags, address + uint32_t nodeSize = llvm::getULEB128Size(_flags) + + llvm::getULEB128Size(_address); + assert(nodeSize < 256); + out.append_byte(nodeSize); + out.append_uleb128(_flags); + out.append_uleb128(_address); + } + } else { + // Node with no export info. + uint32_t nodeSize = 0; + out.append_byte(nodeSize); + } + // Add number of children. + assert(_children.size() < 256); + out.append_byte(_children.size()); + // Append each child edge substring and node offset. + for (TrieEdge &edge : _children) { + out.append_string(edge._subString); + out.append_uleb128(edge._child->_trieOffset); + } +} + +void MachOFileLayout::buildExportTrie() { + if (_file.exportInfo.empty()) + return; + + // For all temporary strings and objects used building trie. + BumpPtrAllocator allocator; + + // Build trie of all exported symbols. + TrieNode* rootNode = new (allocator) TrieNode(StringRef()); + std::vector<TrieNode*> allNodes; + allNodes.reserve(_file.exportInfo.size()*2); + allNodes.push_back(rootNode); + for (const Export& entry : _file.exportInfo) { + rootNode->addSymbol(entry, allocator, allNodes); + } + + // Assign each node in the vector an offset in the trie stream, iterating + // until all uleb128 sizes have stabilized. + bool more; + do { + uint32_t offset = 0; + more = false; + for (TrieNode* node : allNodes) { + if (node->updateOffset(offset)) + more = true; + } + } while (more); + + // Serialize trie to ByteBuffer. + for (TrieNode* node : allNodes) { + node->appendToByteBuffer(_exportTrie); + } + _exportTrie.align(_is64 ? 8 : 4); +} + + +void MachOFileLayout::computeSymbolTableSizes() { + // MachO symbol tables have three ranges: locals, globals, and undefines + const size_t nlistSize = (_is64 ? sizeof(nlist_64) : sizeof(nlist)); + _symbolTableSize = nlistSize * (_file.localSymbols.size() + + _file.globalSymbols.size() + + _file.undefinedSymbols.size()); + _symbolStringPoolSize = 0; + for (const Symbol &sym : _file.localSymbols) { + _symbolStringPoolSize += (sym.name.size()+1); + } + for (const Symbol &sym : _file.globalSymbols) { + _symbolStringPoolSize += (sym.name.size()+1); + } + for (const Symbol &sym : _file.undefinedSymbols) { + _symbolStringPoolSize += (sym.name.size()+1); + } + _symbolTableLocalsStartIndex = 0; + _symbolTableGlobalsStartIndex = _file.localSymbols.size(); + _symbolTableUndefinesStartIndex = _symbolTableGlobalsStartIndex + + _file.globalSymbols.size(); + + _indirectSymbolTableCount = 0; + for (const Section § : _file.sections) { + _indirectSymbolTableCount += sect.indirectSymbols.size(); + } +} + +void MachOFileLayout::computeDataInCodeSize() { + _dataInCodeSize = _file.dataInCode.size() * sizeof(data_in_code_entry); +} + +void MachOFileLayout::writeLinkEditContent() { + if (_file.fileType == llvm::MachO::MH_OBJECT) { + writeRelocations(); + writeDataInCodeInfo(); + writeSymbolTable(); + } else { + writeRebaseInfo(); + writeBindingInfo(); + writeLazyBindingInfo(); + // TODO: add weak binding info + writeExportInfo(); + writeDataInCodeInfo(); + writeSymbolTable(); + } +} + +std::error_code MachOFileLayout::writeBinary(StringRef path) { + // Check for pending error from constructor. + if (_ec) + return _ec; + // Create FileOutputBuffer with calculated size. + std::unique_ptr<llvm::FileOutputBuffer> fob; + unsigned flags = 0; + if (_file.fileType != llvm::MachO::MH_OBJECT) + flags = llvm::FileOutputBuffer::F_executable; + std::error_code ec; + ec = llvm::FileOutputBuffer::create(path, size(), fob, flags); + if (ec) + return ec; + + // Write content. + _buffer = fob->getBufferStart(); + writeMachHeader(); + ec = writeLoadCommands(); + if (ec) + return ec; + writeSectionContent(); + writeLinkEditContent(); + fob->commit(); + + return std::error_code(); +} + + +/// Takes in-memory normalized view and writes a mach-o object file. +std::error_code writeBinary(const NormalizedFile &file, StringRef path) { + MachOFileLayout layout(file); + return layout.writeBinary(path); +} + + +} // namespace normalized +} // namespace mach_o +} // namespace lld + diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp new file mode 100644 index 0000000000000..4d6183f71df75 --- /dev/null +++ b/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp @@ -0,0 +1,1238 @@ +//===- lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp ------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +/// +/// \file Converts from in-memory Atoms to in-memory normalized mach-o. +/// +/// +------------+ +/// | normalized | +/// +------------+ +/// ^ +/// | +/// | +/// +-------+ +/// | Atoms | +/// +-------+ + +#include "MachONormalizedFile.h" +#include "ArchHandler.h" +#include "MachONormalizedFileBinaryUtils.h" +#include "lld/Core/Error.h" +#include "lld/Core/LLVM.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MachO.h" +#include <map> +#include <system_error> + +using llvm::StringRef; +using llvm::isa; +using namespace llvm::MachO; +using namespace lld::mach_o::normalized; +using namespace lld; + +namespace { + +struct AtomInfo { + const DefinedAtom *atom; + uint64_t offsetInSection; +}; + +struct SectionInfo { + SectionInfo(StringRef seg, StringRef sect, SectionType type, + const MachOLinkingContext &ctxt, uint32_t attr=0); + + StringRef segmentName; + StringRef sectionName; + SectionType type; + uint32_t attributes; + uint64_t address; + uint64_t size; + uint32_t alignment; + std::vector<AtomInfo> atomsAndOffsets; + uint32_t normalizedSectionIndex; + uint32_t finalSectionIndex; +}; + +SectionInfo::SectionInfo(StringRef sg, StringRef sct, SectionType t, + const MachOLinkingContext &ctxt, uint32_t attrs) + : segmentName(sg), sectionName(sct), type(t), attributes(attrs), + address(0), size(0), alignment(0), + normalizedSectionIndex(0), finalSectionIndex(0) { + uint8_t align; + if (ctxt.sectionAligned(segmentName, sectionName, align)) { + alignment = align; + } +} + +struct SegmentInfo { + SegmentInfo(StringRef name); + + StringRef name; + uint64_t address; + uint64_t size; + uint32_t access; + std::vector<SectionInfo*> sections; + uint32_t normalizedSegmentIndex; +}; + +SegmentInfo::SegmentInfo(StringRef n) + : name(n), address(0), size(0), access(0), normalizedSegmentIndex(0) { +} + + +class Util { +public: + Util(const MachOLinkingContext &ctxt) + : _context(ctxt), _archHandler(ctxt.archHandler()), _entryAtom(nullptr) {} + ~Util(); + + void assignAtomsToSections(const lld::File &atomFile); + void organizeSections(); + void assignAddressesToSections(const NormalizedFile &file); + uint32_t fileFlags(); + void copySegmentInfo(NormalizedFile &file); + void copySectionInfo(NormalizedFile &file); + void updateSectionInfo(NormalizedFile &file); + void buildAtomToAddressMap(); + std::error_code addSymbols(const lld::File &atomFile, NormalizedFile &file); + void addIndirectSymbols(const lld::File &atomFile, NormalizedFile &file); + void addRebaseAndBindingInfo(const lld::File &, NormalizedFile &file); + void addExportInfo(const lld::File &, NormalizedFile &file); + void addSectionRelocs(const lld::File &, NormalizedFile &file); + void buildDataInCodeArray(const lld::File &, NormalizedFile &file); + void addDependentDylibs(const lld::File &, NormalizedFile &file); + void copyEntryPointAddress(NormalizedFile &file); + void copySectionContent(NormalizedFile &file); + +private: + typedef std::map<DefinedAtom::ContentType, SectionInfo*> TypeToSection; + typedef llvm::DenseMap<const Atom*, uint64_t> AtomToAddress; + + struct DylibInfo { int ordinal; bool hasWeak; bool hasNonWeak; }; + typedef llvm::StringMap<DylibInfo> DylibPathToInfo; + + SectionInfo *sectionForAtom(const DefinedAtom*); + SectionInfo *getRelocatableSection(DefinedAtom::ContentType type); + SectionInfo *getFinalSection(DefinedAtom::ContentType type); + void appendAtom(SectionInfo *sect, const DefinedAtom *atom); + SegmentInfo *segmentForName(StringRef segName); + void layoutSectionsInSegment(SegmentInfo *seg, uint64_t &addr); + void layoutSectionsInTextSegment(size_t, SegmentInfo *, uint64_t &); + void copySectionContent(SectionInfo *si, ContentBytes &content); + uint16_t descBits(const DefinedAtom* atom); + int dylibOrdinal(const SharedLibraryAtom *sa); + void segIndexForSection(const SectionInfo *sect, + uint8_t &segmentIndex, uint64_t &segmentStartAddr); + const Atom *targetOfLazyPointer(const DefinedAtom *lpAtom); + const Atom *targetOfStub(const DefinedAtom *stubAtom); + std::error_code getSymbolTableRegion(const DefinedAtom* atom, + bool &inGlobalsRegion, + SymbolScope &symbolScope); + void appendSection(SectionInfo *si, NormalizedFile &file); + uint32_t sectionIndexForAtom(const Atom *atom); + + static uint64_t alignTo(uint64_t value, uint8_t align2); + typedef llvm::DenseMap<const Atom*, uint32_t> AtomToIndex; + struct AtomAndIndex { const Atom *atom; uint32_t index; SymbolScope scope; }; + struct AtomSorter { + bool operator()(const AtomAndIndex &left, const AtomAndIndex &right); + }; + struct SegmentSorter { + bool operator()(const SegmentInfo *left, const SegmentInfo *right); + static unsigned weight(const SegmentInfo *); + }; + struct TextSectionSorter { + bool operator()(const SectionInfo *left, const SectionInfo *right); + static unsigned weight(const SectionInfo *); + }; + + const MachOLinkingContext &_context; + mach_o::ArchHandler &_archHandler; + llvm::BumpPtrAllocator _allocator; + std::vector<SectionInfo*> _sectionInfos; + std::vector<SegmentInfo*> _segmentInfos; + TypeToSection _sectionMap; + std::vector<SectionInfo*> _customSections; + AtomToAddress _atomToAddress; + DylibPathToInfo _dylibInfo; + const DefinedAtom *_entryAtom; + AtomToIndex _atomToSymbolIndex; + std::vector<const Atom *> _machHeaderAliasAtoms; +}; + +Util::~Util() { + // The SectionInfo structs are BumpPtr allocated, but atomsAndOffsets needs + // to be deleted. + for (SectionInfo *si : _sectionInfos) { + // clear() destroys vector elements, but does not deallocate. + // Instead use swap() to deallocate vector buffer. + std::vector<AtomInfo> empty; + si->atomsAndOffsets.swap(empty); + } + // The SegmentInfo structs are BumpPtr allocated, but sections needs + // to be deleted. + for (SegmentInfo *sgi : _segmentInfos) { + std::vector<SectionInfo*> empty2; + sgi->sections.swap(empty2); + } +} + +SectionInfo *Util::getRelocatableSection(DefinedAtom::ContentType type) { + StringRef segmentName; + StringRef sectionName; + SectionType sectionType; + SectionAttr sectionAttrs; + + // Use same table used by when parsing .o files. + relocatableSectionInfoForContentType(type, segmentName, sectionName, + sectionType, sectionAttrs); + // If we already have a SectionInfo with this name, re-use it. + // This can happen if two ContentType map to the same mach-o section. + for (auto sect : _sectionMap) { + if (sect.second->sectionName.equals(sectionName) && + sect.second->segmentName.equals(segmentName)) { + return sect.second; + } + } + // Otherwise allocate new SectionInfo object. + SectionInfo *sect = new (_allocator) SectionInfo(segmentName, sectionName, + sectionType, _context, + sectionAttrs); + _sectionInfos.push_back(sect); + _sectionMap[type] = sect; + return sect; +} + +#define ENTRY(seg, sect, type, atomType) \ + {seg, sect, type, DefinedAtom::atomType } + +struct MachOFinalSectionFromAtomType { + StringRef segmentName; + StringRef sectionName; + SectionType sectionType; + DefinedAtom::ContentType atomType; +}; + +const MachOFinalSectionFromAtomType sectsToAtomType[] = { + ENTRY("__TEXT", "__text", S_REGULAR, typeCode), + ENTRY("__TEXT", "__cstring", S_CSTRING_LITERALS, typeCString), + ENTRY("__TEXT", "__ustring", S_REGULAR, typeUTF16String), + ENTRY("__TEXT", "__const", S_REGULAR, typeConstant), + ENTRY("__TEXT", "__const", S_4BYTE_LITERALS, typeLiteral4), + ENTRY("__TEXT", "__const", S_8BYTE_LITERALS, typeLiteral8), + ENTRY("__TEXT", "__const", S_16BYTE_LITERALS, typeLiteral16), + ENTRY("__TEXT", "__stubs", S_SYMBOL_STUBS, typeStub), + ENTRY("__TEXT", "__stub_helper", S_REGULAR, typeStubHelper), + ENTRY("__TEXT", "__gcc_except_tab", S_REGULAR, typeLSDA), + ENTRY("__TEXT", "__eh_frame", S_COALESCED, typeCFI), + ENTRY("__TEXT", "__unwind_info", S_REGULAR, typeProcessedUnwindInfo), + ENTRY("__DATA", "__data", S_REGULAR, typeData), + ENTRY("__DATA", "__const", S_REGULAR, typeConstData), + ENTRY("__DATA", "__cfstring", S_REGULAR, typeCFString), + ENTRY("__DATA", "__la_symbol_ptr", S_LAZY_SYMBOL_POINTERS, + typeLazyPointer), + ENTRY("__DATA", "__mod_init_func", S_MOD_INIT_FUNC_POINTERS, + typeInitializerPtr), + ENTRY("__DATA", "__mod_term_func", S_MOD_TERM_FUNC_POINTERS, + typeTerminatorPtr), + ENTRY("__DATA", "__got", S_NON_LAZY_SYMBOL_POINTERS, + typeGOT), + ENTRY("__DATA", "__bss", S_ZEROFILL, typeZeroFill), + ENTRY("__DATA", "__interposing", S_INTERPOSING, typeInterposingTuples), +}; +#undef ENTRY + + +SectionInfo *Util::getFinalSection(DefinedAtom::ContentType atomType) { + for (auto &p : sectsToAtomType) { + if (p.atomType != atomType) + continue; + SectionAttr sectionAttrs = 0; + switch (atomType) { + case DefinedAtom::typeCode: + case DefinedAtom::typeStub: + case DefinedAtom::typeStubHelper: + sectionAttrs = S_ATTR_PURE_INSTRUCTIONS; + break; + default: + break; + } + // If we already have a SectionInfo with this name, re-use it. + // This can happen if two ContentType map to the same mach-o section. + for (auto sect : _sectionMap) { + if (sect.second->sectionName.equals(p.sectionName) && + sect.second->segmentName.equals(p.segmentName)) { + return sect.second; + } + } + // Otherwise allocate new SectionInfo object. + SectionInfo *sect = new (_allocator) SectionInfo(p.segmentName, + p.sectionName, + p.sectionType, + _context, + sectionAttrs); + _sectionInfos.push_back(sect); + _sectionMap[atomType] = sect; + return sect; + } + llvm_unreachable("content type not yet supported"); +} + + + +SectionInfo *Util::sectionForAtom(const DefinedAtom *atom) { + if (atom->sectionChoice() == DefinedAtom::sectionBasedOnContent) { + // Section for this atom is derived from content type. + DefinedAtom::ContentType type = atom->contentType(); + auto pos = _sectionMap.find(type); + if ( pos != _sectionMap.end() ) + return pos->second; + bool rMode = (_context.outputMachOType() == llvm::MachO::MH_OBJECT); + return rMode ? getRelocatableSection(type) : getFinalSection(type); + } else { + // This atom needs to be in a custom section. + StringRef customName = atom->customSectionName(); + // Look to see if we have already allocated the needed custom section. + for(SectionInfo *sect : _customSections) { + const DefinedAtom *firstAtom = sect->atomsAndOffsets.front().atom; + if (firstAtom->customSectionName().equals(customName)) { + return sect; + } + } + // Not found, so need to create a new custom section. + size_t seperatorIndex = customName.find('/'); + assert(seperatorIndex != StringRef::npos); + StringRef segName = customName.slice(0, seperatorIndex); + StringRef sectName = customName.drop_front(seperatorIndex + 1); + SectionInfo *sect = new (_allocator) SectionInfo(segName, sectName, + S_REGULAR, _context); + _customSections.push_back(sect); + _sectionInfos.push_back(sect); + return sect; + } +} + + +void Util::appendAtom(SectionInfo *sect, const DefinedAtom *atom) { + // Figure out offset for atom in this section given alignment constraints. + uint64_t offset = sect->size; + DefinedAtom::Alignment atomAlign = atom->alignment(); + uint64_t align2 = 1 << atomAlign.powerOf2; + uint64_t requiredModulus = atomAlign.modulus; + uint64_t currentModulus = (offset % align2); + if ( currentModulus != requiredModulus ) { + if ( requiredModulus > currentModulus ) + offset += requiredModulus-currentModulus; + else + offset += align2+requiredModulus-currentModulus; + } + // Record max alignment of any atom in this section. + if ( atomAlign.powerOf2 > sect->alignment ) + sect->alignment = atomAlign.powerOf2; + // Assign atom to this section with this offset. + AtomInfo ai = {atom, offset}; + sect->atomsAndOffsets.push_back(ai); + // Update section size to include this atom. + sect->size = offset + atom->size(); +} + +void Util::assignAtomsToSections(const lld::File &atomFile) { + for (const DefinedAtom *atom : atomFile.defined()) { + if (atom->contentType() == DefinedAtom::typeMachHeader) + _machHeaderAliasAtoms.push_back(atom); + else + appendAtom(sectionForAtom(atom), atom); + } +} + +SegmentInfo *Util::segmentForName(StringRef segName) { + for (SegmentInfo *si : _segmentInfos) { + if ( si->name.equals(segName) ) + return si; + } + SegmentInfo *info = new (_allocator) SegmentInfo(segName); + if (segName.equals("__TEXT")) + info->access = VM_PROT_READ | VM_PROT_EXECUTE; + else if (segName.equals("__DATA")) + info->access = VM_PROT_READ | VM_PROT_WRITE; + else if (segName.equals("__PAGEZERO")) + info->access = 0; + _segmentInfos.push_back(info); + return info; +} + +unsigned Util::SegmentSorter::weight(const SegmentInfo *seg) { + return llvm::StringSwitch<unsigned>(seg->name) + .Case("__PAGEZERO", 1) + .Case("__TEXT", 2) + .Case("__DATA", 3) + .Default(100); +} + +bool Util::SegmentSorter::operator()(const SegmentInfo *left, + const SegmentInfo *right) { + return (weight(left) < weight(right)); +} + +unsigned Util::TextSectionSorter::weight(const SectionInfo *sect) { + return llvm::StringSwitch<unsigned>(sect->sectionName) + .Case("__text", 1) + .Case("__stubs", 2) + .Case("__stub_helper", 3) + .Case("__const", 4) + .Case("__cstring", 5) + .Case("__unwind_info", 98) + .Case("__eh_frame", 99) + .Default(10); +} + +bool Util::TextSectionSorter::operator()(const SectionInfo *left, + const SectionInfo *right) { + return (weight(left) < weight(right)); +} + + +void Util::organizeSections() { + if (_context.outputMachOType() == llvm::MachO::MH_OBJECT) { + // Leave sections ordered as normalized file specified. + uint32_t sectionIndex = 1; + for (SectionInfo *si : _sectionInfos) { + si->finalSectionIndex = sectionIndex++; + } + } else { + switch (_context.outputMachOType()){ + case llvm::MachO::MH_EXECUTE: + // Main executables, need a zero-page segment + segmentForName("__PAGEZERO"); + // Fall into next case. + case llvm::MachO::MH_DYLIB: + case llvm::MachO::MH_BUNDLE: + // All dynamic code needs TEXT segment to hold the load commands. + segmentForName("__TEXT"); + break; + default: + break; + } + // Group sections into segments. + for (SectionInfo *si : _sectionInfos) { + SegmentInfo *seg = segmentForName(si->segmentName); + seg->sections.push_back(si); + } + // Sort segments. + std::sort(_segmentInfos.begin(), _segmentInfos.end(), SegmentSorter()); + + // Sort sections within segments. + for (SegmentInfo *seg : _segmentInfos) { + if (seg->name.equals("__TEXT")) { + std::sort(seg->sections.begin(), seg->sections.end(), + TextSectionSorter()); + } + } + + // Record final section indexes. + uint32_t segmentIndex = 0; + uint32_t sectionIndex = 1; + for (SegmentInfo *seg : _segmentInfos) { + seg->normalizedSegmentIndex = segmentIndex++; + for (SectionInfo *sect : seg->sections) { + sect->finalSectionIndex = sectionIndex++; + } + } + } + +} + +uint64_t Util::alignTo(uint64_t value, uint8_t align2) { + return llvm::RoundUpToAlignment(value, 1 << align2); +} + + +void Util::layoutSectionsInSegment(SegmentInfo *seg, uint64_t &addr) { + seg->address = addr; + for (SectionInfo *sect : seg->sections) { + sect->address = alignTo(addr, sect->alignment); + addr = sect->address + sect->size; + } + seg->size = llvm::RoundUpToAlignment(addr - seg->address,_context.pageSize()); +} + + +// __TEXT segment lays out backwards so padding is at front after load commands. +void Util::layoutSectionsInTextSegment(size_t hlcSize, SegmentInfo *seg, + uint64_t &addr) { + seg->address = addr; + // Walks sections starting at end to calculate padding for start. + int64_t taddr = 0; + for (auto it = seg->sections.rbegin(); it != seg->sections.rend(); ++it) { + SectionInfo *sect = *it; + taddr -= sect->size; + taddr = taddr & (0 - (1 << sect->alignment)); + } + int64_t padding = taddr - hlcSize; + while (padding < 0) + padding += _context.pageSize(); + // Start assigning section address starting at padded offset. + addr += (padding + hlcSize); + for (SectionInfo *sect : seg->sections) { + sect->address = alignTo(addr, sect->alignment); + addr = sect->address + sect->size; + } + seg->size = llvm::RoundUpToAlignment(addr - seg->address,_context.pageSize()); +} + + +void Util::assignAddressesToSections(const NormalizedFile &file) { + size_t hlcSize = headerAndLoadCommandsSize(file); + uint64_t address = 0; + if (_context.outputMachOType() != llvm::MachO::MH_OBJECT) { + for (SegmentInfo *seg : _segmentInfos) { + if (seg->name.equals("__PAGEZERO")) { + seg->size = _context.pageZeroSize(); + address += seg->size; + } + else if (seg->name.equals("__TEXT")) { + // _context.baseAddress() == 0 implies it was either unspecified or + // pageZeroSize is also 0. In either case resetting address is safe. + address = _context.baseAddress() ? _context.baseAddress() : address; + layoutSectionsInTextSegment(hlcSize, seg, address); + } else + layoutSectionsInSegment(seg, address); + + address = llvm::RoundUpToAlignment(address, _context.pageSize()); + } + DEBUG_WITH_TYPE("WriterMachO-norm", + llvm::dbgs() << "assignAddressesToSections()\n"; + for (SegmentInfo *sgi : _segmentInfos) { + llvm::dbgs() << " address=" << llvm::format("0x%08llX", sgi->address) + << ", size=" << llvm::format("0x%08llX", sgi->size) + << ", segment-name='" << sgi->name + << "'\n"; + for (SectionInfo *si : sgi->sections) { + llvm::dbgs()<< " addr=" << llvm::format("0x%08llX", si->address) + << ", size=" << llvm::format("0x%08llX", si->size) + << ", section-name='" << si->sectionName + << "\n"; + } + } + ); + } else { + for (SectionInfo *sect : _sectionInfos) { + sect->address = alignTo(address, sect->alignment); + address = sect->address + sect->size; + } + DEBUG_WITH_TYPE("WriterMachO-norm", + llvm::dbgs() << "assignAddressesToSections()\n"; + for (SectionInfo *si : _sectionInfos) { + llvm::dbgs() << " section=" << si->sectionName + << " address= " << llvm::format("0x%08X", si->address) + << " size= " << llvm::format("0x%08X", si->size) + << "\n"; + } + ); + } +} + + +void Util::copySegmentInfo(NormalizedFile &file) { + for (SegmentInfo *sgi : _segmentInfos) { + Segment seg; + seg.name = sgi->name; + seg.address = sgi->address; + seg.size = sgi->size; + seg.access = sgi->access; + file.segments.push_back(seg); + } +} + +void Util::appendSection(SectionInfo *si, NormalizedFile &file) { + // Add new empty section to end of file.sections. + Section temp; + file.sections.push_back(std::move(temp)); + Section* normSect = &file.sections.back(); + // Copy fields to normalized section. + normSect->segmentName = si->segmentName; + normSect->sectionName = si->sectionName; + normSect->type = si->type; + normSect->attributes = si->attributes; + normSect->address = si->address; + normSect->alignment = si->alignment; + // Record where normalized section is. + si->normalizedSectionIndex = file.sections.size()-1; +} + +void Util::copySectionContent(NormalizedFile &file) { + const bool r = (_context.outputMachOType() == llvm::MachO::MH_OBJECT); + + // Utility function for ArchHandler to find address of atom in output file. + auto addrForAtom = [&] (const Atom &atom) -> uint64_t { + auto pos = _atomToAddress.find(&atom); + assert(pos != _atomToAddress.end()); + return pos->second; + }; + + auto sectionAddrForAtom = [&] (const Atom &atom) -> uint64_t { + for (const SectionInfo *sectInfo : _sectionInfos) + for (const AtomInfo &atomInfo : sectInfo->atomsAndOffsets) + if (atomInfo.atom == &atom) + return sectInfo->address; + llvm_unreachable("atom not assigned to section"); + }; + + for (SectionInfo *si : _sectionInfos) { + Section *normSect = &file.sections[si->normalizedSectionIndex]; + if (si->type == llvm::MachO::S_ZEROFILL) { + const uint8_t *empty = nullptr; + normSect->content = llvm::makeArrayRef(empty, si->size); + continue; + } + // Copy content from atoms to content buffer for section. + uint8_t *sectionContent = file.ownedAllocations.Allocate<uint8_t>(si->size); + normSect->content = llvm::makeArrayRef(sectionContent, si->size); + for (AtomInfo &ai : si->atomsAndOffsets) { + uint8_t *atomContent = reinterpret_cast<uint8_t*> + (§ionContent[ai.offsetInSection]); + _archHandler.generateAtomContent(*ai.atom, r, addrForAtom, + sectionAddrForAtom, + _context.baseAddress(), atomContent); + } + } +} + + +void Util::copySectionInfo(NormalizedFile &file) { + file.sections.reserve(_sectionInfos.size()); + // For final linked images, write sections grouped by segment. + if (_context.outputMachOType() != llvm::MachO::MH_OBJECT) { + for (SegmentInfo *sgi : _segmentInfos) { + for (SectionInfo *si : sgi->sections) { + appendSection(si, file); + } + } + } else { + // Object files write sections in default order. + for (SectionInfo *si : _sectionInfos) { + appendSection(si, file); + } + } +} + +void Util::updateSectionInfo(NormalizedFile &file) { + file.sections.reserve(_sectionInfos.size()); + if (_context.outputMachOType() != llvm::MachO::MH_OBJECT) { + // For final linked images, sections grouped by segment. + for (SegmentInfo *sgi : _segmentInfos) { + Segment *normSeg = &file.segments[sgi->normalizedSegmentIndex]; + normSeg->address = sgi->address; + normSeg->size = sgi->size; + for (SectionInfo *si : sgi->sections) { + Section *normSect = &file.sections[si->normalizedSectionIndex]; + normSect->address = si->address; + } + } + } else { + // Object files write sections in default order. + for (SectionInfo *si : _sectionInfos) { + Section *normSect = &file.sections[si->normalizedSectionIndex]; + normSect->address = si->address; + } + } +} + +void Util::copyEntryPointAddress(NormalizedFile &nFile) { + if (_context.outputTypeHasEntry()) { + if (_archHandler.isThumbFunction(*_entryAtom)) + nFile.entryAddress = (_atomToAddress[_entryAtom] | 1); + else + nFile.entryAddress = _atomToAddress[_entryAtom]; + } +} + +void Util::buildAtomToAddressMap() { + DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs() + << "assign atom addresses:\n"); + const bool lookForEntry = _context.outputTypeHasEntry(); + for (SectionInfo *sect : _sectionInfos) { + for (const AtomInfo &info : sect->atomsAndOffsets) { + _atomToAddress[info.atom] = sect->address + info.offsetInSection; + if (lookForEntry && (info.atom->contentType() == DefinedAtom::typeCode) && + (info.atom->size() != 0) && + info.atom->name() == _context.entrySymbolName()) { + _entryAtom = info.atom; + } + DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs() + << " address=" + << llvm::format("0x%016X", _atomToAddress[info.atom]) + << " atom=" << info.atom + << " name=" << info.atom->name() << "\n"); + } + } + for (const Atom *atom : _machHeaderAliasAtoms) { + _atomToAddress[atom] = _context.baseAddress(); + DEBUG_WITH_TYPE("WriterMachO-address", llvm::dbgs() + << " address=" + << llvm::format("0x%016X", _atomToAddress[atom]) + << " atom=" << atom + << " name=" << atom->name() << "\n"); + } +} + +uint16_t Util::descBits(const DefinedAtom* atom) { + uint16_t desc = 0; + switch (atom->merge()) { + case lld::DefinedAtom::mergeNo: + case lld::DefinedAtom::mergeAsTentative: + break; + case lld::DefinedAtom::mergeAsWeak: + case lld::DefinedAtom::mergeAsWeakAndAddressUsed: + desc |= N_WEAK_DEF; + break; + case lld::DefinedAtom::mergeSameNameAndSize: + case lld::DefinedAtom::mergeByLargestSection: + case lld::DefinedAtom::mergeByContent: + llvm_unreachable("Unsupported DefinedAtom::merge()"); + break; + } + if (atom->contentType() == lld::DefinedAtom::typeResolver) + desc |= N_SYMBOL_RESOLVER; + if (_archHandler.isThumbFunction(*atom)) + desc |= N_ARM_THUMB_DEF; + if (atom->deadStrip() == DefinedAtom::deadStripNever) { + if ((atom->contentType() != DefinedAtom::typeInitializerPtr) + && (atom->contentType() != DefinedAtom::typeTerminatorPtr)) + desc |= N_NO_DEAD_STRIP; + } + return desc; +} + + +bool Util::AtomSorter::operator()(const AtomAndIndex &left, + const AtomAndIndex &right) { + return (left.atom->name().compare(right.atom->name()) < 0); +} + + +std::error_code Util::getSymbolTableRegion(const DefinedAtom* atom, + bool &inGlobalsRegion, + SymbolScope &scope) { + bool rMode = (_context.outputMachOType() == llvm::MachO::MH_OBJECT); + switch (atom->scope()) { + case Atom::scopeTranslationUnit: + scope = 0; + inGlobalsRegion = false; + return std::error_code(); + case Atom::scopeLinkageUnit: + if ((_context.exportMode() == MachOLinkingContext::ExportMode::whiteList) + && _context.exportSymbolNamed(atom->name())) { + return make_dynamic_error_code(Twine("cannot export hidden symbol ") + + atom->name()); + } + if (rMode) { + if (_context.keepPrivateExterns()) { + // -keep_private_externs means keep in globals region as N_PEXT. + scope = N_PEXT | N_EXT; + inGlobalsRegion = true; + return std::error_code(); + } + } + // scopeLinkageUnit symbols are no longer global once linked. + scope = N_PEXT; + inGlobalsRegion = false; + return std::error_code(); + case Atom::scopeGlobal: + if (_context.exportRestrictMode()) { + if (_context.exportSymbolNamed(atom->name())) { + scope = N_EXT; + inGlobalsRegion = true; + return std::error_code(); + } else { + scope = N_PEXT; + inGlobalsRegion = false; + return std::error_code(); + } + } else { + scope = N_EXT; + inGlobalsRegion = true; + return std::error_code(); + } + break; + } + llvm_unreachable("atom->scope() unknown enum value"); +} + +std::error_code Util::addSymbols(const lld::File &atomFile, + NormalizedFile &file) { + bool rMode = (_context.outputMachOType() == llvm::MachO::MH_OBJECT); + // Mach-O symbol table has three regions: locals, globals, undefs. + + // Add all local (non-global) symbols in address order + std::vector<AtomAndIndex> globals; + globals.reserve(512); + for (SectionInfo *sect : _sectionInfos) { + for (const AtomInfo &info : sect->atomsAndOffsets) { + const DefinedAtom *atom = info.atom; + if (!atom->name().empty()) { + SymbolScope symbolScope; + bool inGlobalsRegion; + if (auto ec = getSymbolTableRegion(atom, inGlobalsRegion, symbolScope)){ + return ec; + } + if (inGlobalsRegion) { + AtomAndIndex ai = { atom, sect->finalSectionIndex, symbolScope }; + globals.push_back(ai); + } else { + Symbol sym; + sym.name = atom->name(); + sym.type = N_SECT; + sym.scope = symbolScope; + sym.sect = sect->finalSectionIndex; + sym.desc = descBits(atom); + sym.value = _atomToAddress[atom]; + _atomToSymbolIndex[atom] = file.localSymbols.size(); + file.localSymbols.push_back(sym); + } + } else if (rMode && _archHandler.needsLocalSymbolInRelocatableFile(atom)){ + // Create 'Lxxx' labels for anonymous atoms if archHandler says so. + static unsigned tempNum = 1; + char tmpName[16]; + sprintf(tmpName, "L%04u", tempNum++); + StringRef tempRef(tmpName); + Symbol sym; + sym.name = tempRef.copy(file.ownedAllocations); + sym.type = N_SECT; + sym.scope = 0; + sym.sect = sect->finalSectionIndex; + sym.desc = 0; + sym.value = _atomToAddress[atom]; + _atomToSymbolIndex[atom] = file.localSymbols.size(); + file.localSymbols.push_back(sym); + } + } + } + + // Sort global symbol alphabetically, then add to symbol table. + std::sort(globals.begin(), globals.end(), AtomSorter()); + const uint32_t globalStartIndex = file.localSymbols.size(); + for (AtomAndIndex &ai : globals) { + Symbol sym; + sym.name = ai.atom->name(); + sym.type = N_SECT; + sym.scope = ai.scope; + sym.sect = ai.index; + sym.desc = descBits(static_cast<const DefinedAtom*>(ai.atom)); + sym.value = _atomToAddress[ai.atom]; + _atomToSymbolIndex[ai.atom] = globalStartIndex + file.globalSymbols.size(); + file.globalSymbols.push_back(sym); + } + + + // Sort undefined symbol alphabetically, then add to symbol table. + std::vector<AtomAndIndex> undefs; + undefs.reserve(128); + for (const UndefinedAtom *atom : atomFile.undefined()) { + AtomAndIndex ai = { atom, 0, N_EXT }; + undefs.push_back(ai); + } + for (const SharedLibraryAtom *atom : atomFile.sharedLibrary()) { + AtomAndIndex ai = { atom, 0, N_EXT }; + undefs.push_back(ai); + } + std::sort(undefs.begin(), undefs.end(), AtomSorter()); + const uint32_t start = file.globalSymbols.size() + file.localSymbols.size(); + for (AtomAndIndex &ai : undefs) { + Symbol sym; + uint16_t desc = 0; + if (!rMode) { + uint8_t ordinal = dylibOrdinal(dyn_cast<SharedLibraryAtom>(ai.atom)); + llvm::MachO::SET_LIBRARY_ORDINAL(desc, ordinal); + } + sym.name = ai.atom->name(); + sym.type = N_UNDF; + sym.scope = ai.scope; + sym.sect = 0; + sym.desc = desc; + sym.value = 0; + _atomToSymbolIndex[ai.atom] = file.undefinedSymbols.size() + start; + file.undefinedSymbols.push_back(sym); + } + + return std::error_code(); +} + +const Atom *Util::targetOfLazyPointer(const DefinedAtom *lpAtom) { + for (const Reference *ref : *lpAtom) { + if (_archHandler.isLazyPointer(*ref)) { + return ref->target(); + } + } + return nullptr; +} + +const Atom *Util::targetOfStub(const DefinedAtom *stubAtom) { + for (const Reference *ref : *stubAtom) { + if (const Atom *ta = ref->target()) { + if (const DefinedAtom *lpAtom = dyn_cast<DefinedAtom>(ta)) { + const Atom *target = targetOfLazyPointer(lpAtom); + if (target) + return target; + } + } + } + return nullptr; +} + + +void Util::addIndirectSymbols(const lld::File &atomFile, NormalizedFile &file) { + for (SectionInfo *si : _sectionInfos) { + Section &normSect = file.sections[si->normalizedSectionIndex]; + switch (si->type) { + case llvm::MachO::S_NON_LAZY_SYMBOL_POINTERS: + for (const AtomInfo &info : si->atomsAndOffsets) { + bool foundTarget = false; + for (const Reference *ref : *info.atom) { + const Atom *target = ref->target(); + if (target) { + if (isa<const SharedLibraryAtom>(target)) { + uint32_t index = _atomToSymbolIndex[target]; + normSect.indirectSymbols.push_back(index); + foundTarget = true; + } else { + normSect.indirectSymbols.push_back( + llvm::MachO::INDIRECT_SYMBOL_LOCAL); + } + } + } + if (!foundTarget) { + normSect.indirectSymbols.push_back( + llvm::MachO::INDIRECT_SYMBOL_ABS); + } + } + break; + case llvm::MachO::S_LAZY_SYMBOL_POINTERS: + for (const AtomInfo &info : si->atomsAndOffsets) { + const Atom *target = targetOfLazyPointer(info.atom); + if (target) { + uint32_t index = _atomToSymbolIndex[target]; + normSect.indirectSymbols.push_back(index); + } + } + break; + case llvm::MachO::S_SYMBOL_STUBS: + for (const AtomInfo &info : si->atomsAndOffsets) { + const Atom *target = targetOfStub(info.atom); + if (target) { + uint32_t index = _atomToSymbolIndex[target]; + normSect.indirectSymbols.push_back(index); + } + } + break; + default: + break; + } + } + +} + +void Util::addDependentDylibs(const lld::File &atomFile,NormalizedFile &nFile) { + // Scan all imported symbols and build up list of dylibs they are from. + int ordinal = 1; + for (const SharedLibraryAtom *slAtom : atomFile.sharedLibrary()) { + StringRef loadPath = slAtom->loadName(); + DylibPathToInfo::iterator pos = _dylibInfo.find(loadPath); + if (pos == _dylibInfo.end()) { + DylibInfo info; + info.ordinal = ordinal++; + info.hasWeak = slAtom->canBeNullAtRuntime(); + info.hasNonWeak = !info.hasWeak; + _dylibInfo[loadPath] = info; + DependentDylib depInfo; + depInfo.path = loadPath; + depInfo.kind = llvm::MachO::LC_LOAD_DYLIB; + depInfo.currentVersion = _context.dylibCurrentVersion(loadPath); + depInfo.compatVersion = _context.dylibCompatVersion(loadPath); + nFile.dependentDylibs.push_back(depInfo); + } else { + if ( slAtom->canBeNullAtRuntime() ) + pos->second.hasWeak = true; + else + pos->second.hasNonWeak = true; + } + } + // Automatically weak link dylib in which all symbols are weak (canBeNull). + for (DependentDylib &dep : nFile.dependentDylibs) { + DylibInfo &info = _dylibInfo[dep.path]; + if (info.hasWeak && !info.hasNonWeak) + dep.kind = llvm::MachO::LC_LOAD_WEAK_DYLIB; + else if (_context.isUpwardDylib(dep.path)) + dep.kind = llvm::MachO::LC_LOAD_UPWARD_DYLIB; + } +} + + +int Util::dylibOrdinal(const SharedLibraryAtom *sa) { + return _dylibInfo[sa->loadName()].ordinal; +} + +void Util::segIndexForSection(const SectionInfo *sect, uint8_t &segmentIndex, + uint64_t &segmentStartAddr) { + segmentIndex = 0; + for (const SegmentInfo *seg : _segmentInfos) { + if ((seg->address <= sect->address) + && (seg->address+seg->size >= sect->address+sect->size)) { + segmentStartAddr = seg->address; + return; + } + ++segmentIndex; + } + llvm_unreachable("section not in any segment"); +} + + +uint32_t Util::sectionIndexForAtom(const Atom *atom) { + uint64_t address = _atomToAddress[atom]; + uint32_t index = 1; + for (const SectionInfo *si : _sectionInfos) { + if ((si->address <= address) && (address < si->address+si->size)) + return index; + ++index; + } + llvm_unreachable("atom not in any section"); +} + +void Util::addSectionRelocs(const lld::File &, NormalizedFile &file) { + if (_context.outputMachOType() != llvm::MachO::MH_OBJECT) + return; + + + // Utility function for ArchHandler to find symbol index for an atom. + auto symIndexForAtom = [&] (const Atom &atom) -> uint32_t { + auto pos = _atomToSymbolIndex.find(&atom); + assert(pos != _atomToSymbolIndex.end()); + return pos->second; + }; + + // Utility function for ArchHandler to find section index for an atom. + auto sectIndexForAtom = [&] (const Atom &atom) -> uint32_t { + return sectionIndexForAtom(&atom); + }; + + // Utility function for ArchHandler to find address of atom in output file. + auto addressForAtom = [&] (const Atom &atom) -> uint64_t { + auto pos = _atomToAddress.find(&atom); + assert(pos != _atomToAddress.end()); + return pos->second; + }; + + for (SectionInfo *si : _sectionInfos) { + Section &normSect = file.sections[si->normalizedSectionIndex]; + for (const AtomInfo &info : si->atomsAndOffsets) { + const DefinedAtom *atom = info.atom; + for (const Reference *ref : *atom) { + _archHandler.appendSectionRelocations(*atom, info.offsetInSection, *ref, + symIndexForAtom, + sectIndexForAtom, + addressForAtom, + normSect.relocations); + } + } + } +} + +void Util::buildDataInCodeArray(const lld::File &, NormalizedFile &file) { + for (SectionInfo *si : _sectionInfos) { + for (const AtomInfo &info : si->atomsAndOffsets) { + // Atoms that contain data-in-code have "transition" references + // which mark a point where the embedded data starts of ends. + // This needs to be converted to the mach-o format which is an array + // of data-in-code ranges. + uint32_t startOffset = 0; + DataRegionType mode = DataRegionType(0); + for (const Reference *ref : *info.atom) { + if (ref->kindNamespace() != Reference::KindNamespace::mach_o) + continue; + if (_archHandler.isDataInCodeTransition(ref->kindValue())) { + DataRegionType nextMode = (DataRegionType)ref->addend(); + if (mode != nextMode) { + if (mode != 0) { + // Found end data range, so make range entry. + DataInCode entry; + entry.offset = si->address + info.offsetInSection + startOffset; + entry.length = ref->offsetInAtom() - startOffset; + entry.kind = mode; + file.dataInCode.push_back(entry); + } + } + mode = nextMode; + startOffset = ref->offsetInAtom(); + } + } + if (mode != 0) { + // Function ends with data (no end transition). + DataInCode entry; + entry.offset = si->address + info.offsetInSection + startOffset; + entry.length = info.atom->size() - startOffset; + entry.kind = mode; + file.dataInCode.push_back(entry); + } + } + } +} + +void Util::addRebaseAndBindingInfo(const lld::File &atomFile, + NormalizedFile &nFile) { + if (_context.outputMachOType() == llvm::MachO::MH_OBJECT) + return; + + uint8_t segmentIndex; + uint64_t segmentStartAddr; + for (SectionInfo *sect : _sectionInfos) { + segIndexForSection(sect, segmentIndex, segmentStartAddr); + for (const AtomInfo &info : sect->atomsAndOffsets) { + const DefinedAtom *atom = info.atom; + for (const Reference *ref : *atom) { + uint64_t segmentOffset = _atomToAddress[atom] + ref->offsetInAtom() + - segmentStartAddr; + const Atom* targ = ref->target(); + if (_archHandler.isPointer(*ref)) { + // A pointer to a DefinedAtom requires rebasing. + if (isa<DefinedAtom>(targ)) { + RebaseLocation rebase; + rebase.segIndex = segmentIndex; + rebase.segOffset = segmentOffset; + rebase.kind = llvm::MachO::REBASE_TYPE_POINTER; + nFile.rebasingInfo.push_back(rebase); + } + // A pointer to an SharedLibraryAtom requires binding. + if (const SharedLibraryAtom *sa = dyn_cast<SharedLibraryAtom>(targ)) { + BindLocation bind; + bind.segIndex = segmentIndex; + bind.segOffset = segmentOffset; + bind.kind = llvm::MachO::BIND_TYPE_POINTER; + bind.canBeNull = sa->canBeNullAtRuntime(); + bind.ordinal = dylibOrdinal(sa); + bind.symbolName = targ->name(); + bind.addend = ref->addend(); + nFile.bindingInfo.push_back(bind); + } + } + else if (_archHandler.isLazyPointer(*ref)) { + BindLocation bind; + if (const SharedLibraryAtom *sa = dyn_cast<SharedLibraryAtom>(targ)) { + bind.ordinal = dylibOrdinal(sa); + } else { + bind.ordinal = llvm::MachO::BIND_SPECIAL_DYLIB_SELF; + } + bind.segIndex = segmentIndex; + bind.segOffset = segmentOffset; + bind.kind = llvm::MachO::BIND_TYPE_POINTER; + bind.canBeNull = false; //sa->canBeNullAtRuntime(); + bind.symbolName = targ->name(); + bind.addend = ref->addend(); + nFile.lazyBindingInfo.push_back(bind); + } + } + } + } +} + +void Util::addExportInfo(const lld::File &atomFile, NormalizedFile &nFile) { + if (_context.outputMachOType() == llvm::MachO::MH_OBJECT) + return; + + for (SectionInfo *sect : _sectionInfos) { + for (const AtomInfo &info : sect->atomsAndOffsets) { + const DefinedAtom *atom = info.atom; + if (atom->scope() != Atom::scopeGlobal) + continue; + if (_context.exportRestrictMode()) { + if (!_context.exportSymbolNamed(atom->name())) + continue; + } + Export exprt; + exprt.name = atom->name(); + exprt.offset = _atomToAddress[atom]; // FIXME: subtract base address + exprt.kind = EXPORT_SYMBOL_FLAGS_KIND_REGULAR; + if (atom->merge() == DefinedAtom::mergeAsWeak) + exprt.flags = EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; + else + exprt.flags = 0; + exprt.otherOffset = 0; + exprt.otherName = StringRef(); + nFile.exportInfo.push_back(exprt); + } + } +} + +uint32_t Util::fileFlags() { + // FIXME: these need to determined at runtime. + if (_context.outputMachOType() == MH_OBJECT) { + return MH_SUBSECTIONS_VIA_SYMBOLS; + } else { + if ((_context.outputMachOType() == MH_EXECUTE) && _context.PIE()) + return MH_DYLDLINK | MH_NOUNDEFS | MH_TWOLEVEL | MH_PIE; + else + return MH_DYLDLINK | MH_NOUNDEFS | MH_TWOLEVEL; + } +} + +} // end anonymous namespace + + +namespace lld { +namespace mach_o { +namespace normalized { + +/// Convert a set of Atoms into a normalized mach-o file. +ErrorOr<std::unique_ptr<NormalizedFile>> +normalizedFromAtoms(const lld::File &atomFile, + const MachOLinkingContext &context) { + // The util object buffers info until the normalized file can be made. + Util util(context); + util.assignAtomsToSections(atomFile); + util.organizeSections(); + + std::unique_ptr<NormalizedFile> f(new NormalizedFile()); + NormalizedFile &normFile = *f.get(); + normFile.arch = context.arch(); + normFile.fileType = context.outputMachOType(); + normFile.flags = util.fileFlags(); + normFile.installName = context.installName(); + normFile.currentVersion = context.currentVersion(); + normFile.compatVersion = context.compatibilityVersion(); + normFile.pageSize = context.pageSize(); + normFile.rpaths = context.rpaths(); + util.addDependentDylibs(atomFile, normFile); + util.copySegmentInfo(normFile); + util.copySectionInfo(normFile); + util.assignAddressesToSections(normFile); + util.buildAtomToAddressMap(); + util.updateSectionInfo(normFile); + util.copySectionContent(normFile); + if (auto ec = util.addSymbols(atomFile, normFile)) { + return ec; + } + util.addIndirectSymbols(atomFile, normFile); + util.addRebaseAndBindingInfo(atomFile, normFile); + util.addExportInfo(atomFile, normFile); + util.addSectionRelocs(atomFile, normFile); + util.buildDataInCodeArray(atomFile, normFile); + util.copyEntryPointAddress(normFile); + + return std::move(f); +} + + +} // namespace normalized +} // namespace mach_o +} // namespace lld + diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp new file mode 100644 index 0000000000000..124e0eaffeeb4 --- /dev/null +++ b/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp @@ -0,0 +1,911 @@ +//===- lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp --------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +/// +/// \file Converts from in-memory normalized mach-o to in-memory Atoms. +/// +/// +------------+ +/// | normalized | +/// +------------+ +/// | +/// | +/// v +/// +-------+ +/// | Atoms | +/// +-------+ + +#include "MachONormalizedFile.h" +#include "ArchHandler.h" +#include "Atoms.h" +#include "File.h" +#include "MachONormalizedFileBinaryUtils.h" +#include "lld/Core/Error.h" +#include "lld/Core/LLVM.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MachO.h" + +using namespace llvm::MachO; +using namespace lld::mach_o::normalized; + +namespace lld { +namespace mach_o { + + +namespace { // anonymous + + +#define ENTRY(seg, sect, type, atomType) \ + {seg, sect, type, DefinedAtom::atomType } + +struct MachORelocatableSectionToAtomType { + StringRef segmentName; + StringRef sectionName; + SectionType sectionType; + DefinedAtom::ContentType atomType; +}; + +const MachORelocatableSectionToAtomType sectsToAtomType[] = { + ENTRY("__TEXT", "__text", S_REGULAR, typeCode), + ENTRY("__TEXT", "__text", S_REGULAR, typeResolver), + ENTRY("__TEXT", "__cstring", S_CSTRING_LITERALS, typeCString), + ENTRY("", "", S_CSTRING_LITERALS, typeCString), + ENTRY("__TEXT", "__ustring", S_REGULAR, typeUTF16String), + ENTRY("__TEXT", "__const", S_REGULAR, typeConstant), + ENTRY("__TEXT", "__const_coal", S_COALESCED, typeConstant), + ENTRY("__TEXT", "__eh_frame", S_COALESCED, typeCFI), + ENTRY("__TEXT", "__eh_frame", S_REGULAR, typeCFI), + ENTRY("__TEXT", "__literal4", S_4BYTE_LITERALS, typeLiteral4), + ENTRY("__TEXT", "__literal8", S_8BYTE_LITERALS, typeLiteral8), + ENTRY("__TEXT", "__literal16", S_16BYTE_LITERALS, typeLiteral16), + ENTRY("__TEXT", "__gcc_except_tab", S_REGULAR, typeLSDA), + ENTRY("__DATA", "__data", S_REGULAR, typeData), + ENTRY("__DATA", "__datacoal_nt", S_COALESCED, typeData), + ENTRY("__DATA", "__const", S_REGULAR, typeConstData), + ENTRY("__DATA", "__cfstring", S_REGULAR, typeCFString), + ENTRY("__DATA", "__mod_init_func", S_MOD_INIT_FUNC_POINTERS, + typeInitializerPtr), + ENTRY("__DATA", "__mod_term_func", S_MOD_TERM_FUNC_POINTERS, + typeTerminatorPtr), + ENTRY("__DATA", "__got", S_NON_LAZY_SYMBOL_POINTERS, + typeGOT), + ENTRY("__DATA", "__bss", S_ZEROFILL, typeZeroFill), + ENTRY("", "", S_NON_LAZY_SYMBOL_POINTERS, + typeGOT), + ENTRY("__DATA", "__interposing", S_INTERPOSING, typeInterposingTuples), + ENTRY("", "", S_INTERPOSING, typeInterposingTuples), + ENTRY("__LD", "__compact_unwind", S_REGULAR, + typeCompactUnwindInfo), + ENTRY("", "", S_REGULAR, typeUnknown) +}; +#undef ENTRY + + +/// Figures out ContentType of a mach-o section. +DefinedAtom::ContentType atomTypeFromSection(const Section §ion, + bool &customSectionName) { + // First look for match of name and type. Empty names in table are wildcards. + customSectionName = false; + for (const MachORelocatableSectionToAtomType *p = sectsToAtomType ; + p->atomType != DefinedAtom::typeUnknown; ++p) { + if (p->sectionType != section.type) + continue; + if (!p->segmentName.equals(section.segmentName) && !p->segmentName.empty()) + continue; + if (!p->sectionName.equals(section.sectionName) && !p->sectionName.empty()) + continue; + customSectionName = p->segmentName.empty() && p->sectionName.empty(); + return p->atomType; + } + // Look for code denoted by section attributes + if (section.attributes & S_ATTR_PURE_INSTRUCTIONS) + return DefinedAtom::typeCode; + + return DefinedAtom::typeUnknown; +} + +enum AtomizeModel { + atomizeAtSymbols, + atomizeFixedSize, + atomizePointerSize, + atomizeUTF8, + atomizeUTF16, + atomizeCFI, + atomizeCU, + atomizeCFString +}; + +/// Returns info on how to atomize a section of the specified ContentType. +void sectionParseInfo(DefinedAtom::ContentType atomType, + unsigned int &sizeMultiple, + DefinedAtom::Scope &scope, + DefinedAtom::Merge &merge, + AtomizeModel &atomizeModel) { + struct ParseInfo { + DefinedAtom::ContentType atomType; + unsigned int sizeMultiple; + DefinedAtom::Scope scope; + DefinedAtom::Merge merge; + AtomizeModel atomizeModel; + }; + + #define ENTRY(type, size, scope, merge, model) \ + {DefinedAtom::type, size, DefinedAtom::scope, DefinedAtom::merge, model } + + static const ParseInfo parseInfo[] = { + ENTRY(typeCode, 1, scopeGlobal, mergeNo, + atomizeAtSymbols), + ENTRY(typeData, 1, scopeGlobal, mergeNo, + atomizeAtSymbols), + ENTRY(typeConstData, 1, scopeGlobal, mergeNo, + atomizeAtSymbols), + ENTRY(typeZeroFill, 1, scopeGlobal, mergeNo, + atomizeAtSymbols), + ENTRY(typeConstant, 1, scopeGlobal, mergeNo, + atomizeAtSymbols), + ENTRY(typeCString, 1, scopeLinkageUnit, mergeByContent, + atomizeUTF8), + ENTRY(typeUTF16String, 1, scopeLinkageUnit, mergeByContent, + atomizeUTF16), + ENTRY(typeCFI, 4, scopeTranslationUnit, mergeNo, + atomizeCFI), + ENTRY(typeLiteral4, 4, scopeLinkageUnit, mergeByContent, + atomizeFixedSize), + ENTRY(typeLiteral8, 8, scopeLinkageUnit, mergeByContent, + atomizeFixedSize), + ENTRY(typeLiteral16, 16, scopeLinkageUnit, mergeByContent, + atomizeFixedSize), + ENTRY(typeCFString, 4, scopeLinkageUnit, mergeByContent, + atomizeCFString), + ENTRY(typeInitializerPtr, 4, scopeTranslationUnit, mergeNo, + atomizePointerSize), + ENTRY(typeTerminatorPtr, 4, scopeTranslationUnit, mergeNo, + atomizePointerSize), + ENTRY(typeCompactUnwindInfo, 4, scopeTranslationUnit, mergeNo, + atomizeCU), + ENTRY(typeGOT, 4, scopeLinkageUnit, mergeByContent, + atomizePointerSize), + ENTRY(typeUnknown, 1, scopeGlobal, mergeNo, + atomizeAtSymbols) + }; + #undef ENTRY + const int tableLen = sizeof(parseInfo) / sizeof(ParseInfo); + for (int i=0; i < tableLen; ++i) { + if (parseInfo[i].atomType == atomType) { + sizeMultiple = parseInfo[i].sizeMultiple; + scope = parseInfo[i].scope; + merge = parseInfo[i].merge; + atomizeModel = parseInfo[i].atomizeModel; + return; + } + } + + // Unknown type is atomized by symbols. + sizeMultiple = 1; + scope = DefinedAtom::scopeGlobal; + merge = DefinedAtom::mergeNo; + atomizeModel = atomizeAtSymbols; +} + + +Atom::Scope atomScope(uint8_t scope) { + switch (scope) { + case N_EXT: + return Atom::scopeGlobal; + case N_PEXT: + case N_PEXT | N_EXT: + return Atom::scopeLinkageUnit; + case 0: + return Atom::scopeTranslationUnit; + } + llvm_unreachable("unknown scope value!"); +} + +void appendSymbolsInSection(const std::vector<Symbol> &inSymbols, + uint32_t sectionIndex, + SmallVector<const Symbol *, 64> &outSyms) { + for (const Symbol &sym : inSymbols) { + // Only look at definition symbols. + if ((sym.type & N_TYPE) != N_SECT) + continue; + if (sym.sect != sectionIndex) + continue; + outSyms.push_back(&sym); + } +} + +void atomFromSymbol(DefinedAtom::ContentType atomType, const Section §ion, + MachOFile &file, uint64_t symbolAddr, StringRef symbolName, + uint16_t symbolDescFlags, Atom::Scope symbolScope, + uint64_t nextSymbolAddr, bool scatterable, bool copyRefs) { + // Mach-O symbol table does have size in it. Instead the size is the + // difference between this and the next symbol. + uint64_t size = nextSymbolAddr - symbolAddr; + uint64_t offset = symbolAddr - section.address; + bool noDeadStrip = (symbolDescFlags & N_NO_DEAD_STRIP) || !scatterable; + if (section.type == llvm::MachO::S_ZEROFILL) { + file.addZeroFillDefinedAtom(symbolName, symbolScope, offset, size, + noDeadStrip, copyRefs, §ion); + } else { + DefinedAtom::Merge merge = (symbolDescFlags & N_WEAK_DEF) + ? DefinedAtom::mergeAsWeak : DefinedAtom::mergeNo; + bool thumb = (symbolDescFlags & N_ARM_THUMB_DEF); + if (atomType == DefinedAtom::typeUnknown) { + // Mach-O needs a segment and section name. Concatentate those two + // with a / separator (e.g. "seg/sect") to fit into the lld model + // of just a section name. + std::string segSectName = section.segmentName.str() + + "/" + section.sectionName.str(); + file.addDefinedAtomInCustomSection(symbolName, symbolScope, atomType, + merge, thumb, noDeadStrip, offset, + size, segSectName, true, §ion); + } else { + if ((atomType == lld::DefinedAtom::typeCode) && + (symbolDescFlags & N_SYMBOL_RESOLVER)) { + atomType = lld::DefinedAtom::typeResolver; + } + file.addDefinedAtom(symbolName, symbolScope, atomType, merge, + offset, size, thumb, noDeadStrip, copyRefs, §ion); + } + } +} + +std::error_code processSymboledSection(DefinedAtom::ContentType atomType, + const Section §ion, + const NormalizedFile &normalizedFile, + MachOFile &file, bool scatterable, + bool copyRefs) { + // Find section's index. + uint32_t sectIndex = 1; + for (auto § : normalizedFile.sections) { + if (§ == §ion) + break; + ++sectIndex; + } + + // Find all symbols in this section. + SmallVector<const Symbol *, 64> symbols; + appendSymbolsInSection(normalizedFile.globalSymbols, sectIndex, symbols); + appendSymbolsInSection(normalizedFile.localSymbols, sectIndex, symbols); + + // Sort symbols. + std::sort(symbols.begin(), symbols.end(), + [](const Symbol *lhs, const Symbol *rhs) -> bool { + if (lhs == rhs) + return false; + // First by address. + uint64_t lhsAddr = lhs->value; + uint64_t rhsAddr = rhs->value; + if (lhsAddr != rhsAddr) + return lhsAddr < rhsAddr; + // If same address, one is an alias so sort by scope. + Atom::Scope lScope = atomScope(lhs->scope); + Atom::Scope rScope = atomScope(rhs->scope); + if (lScope != rScope) + return lScope < rScope; + // If same address and scope, see if one might be better as + // the alias. + bool lPrivate = (lhs->name.front() == 'l'); + bool rPrivate = (rhs->name.front() == 'l'); + if (lPrivate != rPrivate) + return lPrivate; + // If same address and scope, sort by name. + return lhs->name < rhs->name; + }); + + // Debug logging of symbols. + //for (const Symbol *sym : symbols) + // llvm::errs() << " sym: " + // << llvm::format("0x%08llx ", (uint64_t)sym->value) + // << ", " << sym->name << "\n"; + + // If section has no symbols and no content, there are no atoms. + if (symbols.empty() && section.content.empty()) + return std::error_code(); + + if (symbols.empty()) { + // Section has no symbols, put all content in one anoymous atom. + atomFromSymbol(atomType, section, file, section.address, StringRef(), + 0, Atom::scopeTranslationUnit, + section.address + section.content.size(), + scatterable, copyRefs); + } + else if (symbols.front()->value != section.address) { + // Section has anonymous content before first symbol. + atomFromSymbol(atomType, section, file, section.address, StringRef(), + 0, Atom::scopeTranslationUnit, symbols.front()->value, + scatterable, copyRefs); + } + + const Symbol *lastSym = nullptr; + for (const Symbol *sym : symbols) { + if (lastSym != nullptr) { + // Ignore any assembler added "ltmpNNN" symbol at start of section + // if there is another symbol at the start. + if ((lastSym->value != sym->value) + || lastSym->value != section.address + || !lastSym->name.startswith("ltmp")) { + atomFromSymbol(atomType, section, file, lastSym->value, lastSym->name, + lastSym->desc, atomScope(lastSym->scope), sym->value, + scatterable, copyRefs); + } + } + lastSym = sym; + } + if (lastSym != nullptr) { + atomFromSymbol(atomType, section, file, lastSym->value, lastSym->name, + lastSym->desc, atomScope(lastSym->scope), + section.address + section.content.size(), + scatterable, copyRefs); + } + + // If object built without .subsections_via_symbols, add reference chain. + if (!scatterable) { + MachODefinedAtom *prevAtom = nullptr; + file.eachAtomInSection(section, + [&](MachODefinedAtom *atom, uint64_t offset)->void { + if (prevAtom) + prevAtom->addReference(0, Reference::kindLayoutAfter, atom, 0, + Reference::KindArch::all, + Reference::KindNamespace::all); + prevAtom = atom; + }); + } + + return std::error_code(); +} + +std::error_code processSection(DefinedAtom::ContentType atomType, + const Section §ion, + bool customSectionName, + const NormalizedFile &normalizedFile, + MachOFile &file, bool scatterable, + bool copyRefs) { + const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch); + const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); + + // Get info on how to atomize section. + unsigned int sizeMultiple; + DefinedAtom::Scope scope; + DefinedAtom::Merge merge; + AtomizeModel atomizeModel; + sectionParseInfo(atomType, sizeMultiple, scope, merge, atomizeModel); + + // Validate section size. + if ((section.content.size() % sizeMultiple) != 0) + return make_dynamic_error_code(Twine("Section ") + section.segmentName + + "/" + section.sectionName + + " has size (" + + Twine(section.content.size()) + + ") which is not a multiple of " + + Twine(sizeMultiple) ); + + if (atomizeModel == atomizeAtSymbols) { + // Break section up into atoms each with a fixed size. + return processSymboledSection(atomType, section, normalizedFile, file, + scatterable, copyRefs); + } else { + unsigned int size; + for (unsigned int offset = 0, e = section.content.size(); offset != e;) { + switch (atomizeModel) { + case atomizeFixedSize: + // Break section up into atoms each with a fixed size. + size = sizeMultiple; + break; + case atomizePointerSize: + // Break section up into atoms each the size of a pointer. + size = is64 ? 8 : 4; + break; + case atomizeUTF8: + // Break section up into zero terminated c-strings. + size = 0; + for (unsigned int i = offset; i < e; ++i) { + if (section.content[i] == 0) { + size = i + 1 - offset; + break; + } + } + break; + case atomizeUTF16: + // Break section up into zero terminated UTF16 strings. + size = 0; + for (unsigned int i = offset; i < e; i += 2) { + if ((section.content[i] == 0) && (section.content[i + 1] == 0)) { + size = i + 2 - offset; + break; + } + } + break; + case atomizeCFI: + // Break section up into dwarf unwind CFIs (FDE or CIE). + size = read32(§ion.content[offset], isBig) + 4; + if (offset+size > section.content.size()) { + return make_dynamic_error_code(Twine(Twine("Section ") + + section.segmentName + + "/" + section.sectionName + + " is malformed. Size of CFI " + "starting at offset (" + + Twine(offset) + + ") is past end of section.")); + } + break; + case atomizeCU: + // Break section up into compact unwind entries. + size = is64 ? 32 : 20; + break; + case atomizeCFString: + // Break section up into NS/CFString objects. + size = is64 ? 32 : 16; + break; + case atomizeAtSymbols: + break; + } + if (size == 0) { + return make_dynamic_error_code(Twine("Section ") + section.segmentName + + "/" + section.sectionName + + " is malformed. The last atom is " + "not zero terminated."); + } + if (customSectionName) { + // Mach-O needs a segment and section name. Concatentate those two + // with a / separator (e.g. "seg/sect") to fit into the lld model + // of just a section name. + std::string segSectName = section.segmentName.str() + + "/" + section.sectionName.str(); + file.addDefinedAtomInCustomSection(StringRef(), scope, atomType, + merge, false, false, offset, + size, segSectName, true, §ion); + } else { + file.addDefinedAtom(StringRef(), scope, atomType, merge, offset, size, + false, false, copyRefs, §ion); + } + offset += size; + } + } + return std::error_code(); +} + +const Section* findSectionCoveringAddress(const NormalizedFile &normalizedFile, + uint64_t address) { + for (const Section &s : normalizedFile.sections) { + uint64_t sAddr = s.address; + if ((sAddr <= address) && (address < sAddr+s.content.size())) { + return &s; + } + } + return nullptr; +} + +const MachODefinedAtom * +findAtomCoveringAddress(const NormalizedFile &normalizedFile, MachOFile &file, + uint64_t addr, Reference::Addend *addend) { + const Section *sect = nullptr; + sect = findSectionCoveringAddress(normalizedFile, addr); + if (!sect) + return nullptr; + + uint32_t offsetInTarget; + uint64_t offsetInSect = addr - sect->address; + auto atom = + file.findAtomCoveringAddress(*sect, offsetInSect, &offsetInTarget); + *addend = offsetInTarget; + return atom; +} + +// Walks all relocations for a section in a normalized .o file and +// creates corresponding lld::Reference objects. +std::error_code convertRelocs(const Section §ion, + const NormalizedFile &normalizedFile, + bool scatterable, + MachOFile &file, + ArchHandler &handler) { + // Utility function for ArchHandler to find atom by its address. + auto atomByAddr = [&] (uint32_t sectIndex, uint64_t addr, + const lld::Atom **atom, Reference::Addend *addend) + -> std::error_code { + if (sectIndex > normalizedFile.sections.size()) + return make_dynamic_error_code(Twine("out of range section " + "index (") + Twine(sectIndex) + ")"); + const Section *sect = nullptr; + if (sectIndex == 0) { + sect = findSectionCoveringAddress(normalizedFile, addr); + if (!sect) + return make_dynamic_error_code(Twine("address (" + Twine(addr) + + ") is not in any section")); + } else { + sect = &normalizedFile.sections[sectIndex-1]; + } + uint32_t offsetInTarget; + uint64_t offsetInSect = addr - sect->address; + *atom = file.findAtomCoveringAddress(*sect, offsetInSect, &offsetInTarget); + *addend = offsetInTarget; + return std::error_code(); + }; + + // Utility function for ArchHandler to find atom by its symbol index. + auto atomBySymbol = [&] (uint32_t symbolIndex, const lld::Atom **result) + -> std::error_code { + // Find symbol from index. + const Symbol *sym = nullptr; + uint32_t numLocal = normalizedFile.localSymbols.size(); + uint32_t numGlobal = normalizedFile.globalSymbols.size(); + uint32_t numUndef = normalizedFile.undefinedSymbols.size(); + if (symbolIndex < numLocal) { + sym = &normalizedFile.localSymbols[symbolIndex]; + } else if (symbolIndex < numLocal+numGlobal) { + sym = &normalizedFile.globalSymbols[symbolIndex-numLocal]; + } else if (symbolIndex < numLocal+numGlobal+numUndef) { + sym = &normalizedFile.undefinedSymbols[symbolIndex-numLocal-numGlobal]; + } else { + return make_dynamic_error_code(Twine("symbol index (") + + Twine(symbolIndex) + ") out of range"); + } + // Find atom from symbol. + if ((sym->type & N_TYPE) == N_SECT) { + if (sym->sect > normalizedFile.sections.size()) + return make_dynamic_error_code(Twine("symbol section index (") + + Twine(sym->sect) + ") out of range "); + const Section &symSection = normalizedFile.sections[sym->sect-1]; + uint64_t targetOffsetInSect = sym->value - symSection.address; + MachODefinedAtom *target = file.findAtomCoveringAddress(symSection, + targetOffsetInSect); + if (target) { + *result = target; + return std::error_code(); + } + return make_dynamic_error_code(Twine("no atom found for defined symbol")); + } else if ((sym->type & N_TYPE) == N_UNDF) { + const lld::Atom *target = file.findUndefAtom(sym->name); + if (target) { + *result = target; + return std::error_code(); + } + return make_dynamic_error_code(Twine("no undefined atom found for sym")); + } else { + // Search undefs + return make_dynamic_error_code(Twine("no atom found for symbol")); + } + }; + + const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); + // Use old-school iterator so that paired relocations can be grouped. + for (auto it=section.relocations.begin(), e=section.relocations.end(); + it != e; ++it) { + const Relocation &reloc = *it; + // Find atom this relocation is in. + if (reloc.offset > section.content.size()) + return make_dynamic_error_code(Twine("r_address (") + Twine(reloc.offset) + + ") is larger than section size (" + + Twine(section.content.size()) + ")"); + uint32_t offsetInAtom; + MachODefinedAtom *inAtom = file.findAtomCoveringAddress(section, + reloc.offset, + &offsetInAtom); + assert(inAtom && "r_address in range, should have found atom"); + uint64_t fixupAddress = section.address + reloc.offset; + + const lld::Atom *target = nullptr; + Reference::Addend addend = 0; + Reference::KindValue kind; + std::error_code relocErr; + if (handler.isPairedReloc(reloc)) { + // Handle paired relocations together. + relocErr = handler.getPairReferenceInfo( + reloc, *++it, inAtom, offsetInAtom, fixupAddress, isBig, scatterable, + atomByAddr, atomBySymbol, &kind, &target, &addend); + } + else { + // Use ArchHandler to convert relocation record into information + // needed to instantiate an lld::Reference object. + relocErr = handler.getReferenceInfo( + reloc, inAtom, offsetInAtom, fixupAddress, isBig, atomByAddr, + atomBySymbol, &kind, &target, &addend); + } + if (relocErr) { + return make_dynamic_error_code( + Twine("bad relocation (") + relocErr.message() + + ") in section " + + section.segmentName + "/" + section.sectionName + + " (r_address=" + Twine::utohexstr(reloc.offset) + + ", r_type=" + Twine(reloc.type) + + ", r_extern=" + Twine(reloc.isExtern) + + ", r_length=" + Twine((int)reloc.length) + + ", r_pcrel=" + Twine(reloc.pcRel) + + (!reloc.scattered ? (Twine(", r_symbolnum=") + Twine(reloc.symbol)) + : (Twine(", r_scattered=1, r_value=") + + Twine(reloc.value))) + + ")" ); + } else { + // Instantiate an lld::Reference object and add to its atom. + inAtom->addReference(offsetInAtom, kind, target, addend, + handler.kindArch()); + } + } + + return std::error_code(); +} + +bool isDebugInfoSection(const Section §ion) { + if ((section.attributes & S_ATTR_DEBUG) == 0) + return false; + return section.segmentName.equals("__DWARF"); +} + +static int64_t readSPtr(bool is64, bool isBig, const uint8_t *addr) { + if (is64) + return read64(addr, isBig); + + int32_t res = read32(addr, isBig); + return res; +} + +std::error_code addEHFrameReferences(const NormalizedFile &normalizedFile, + MachOFile &file, + mach_o::ArchHandler &handler) { + const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch); + const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch); + + const Section *ehFrameSection = nullptr; + for (auto §ion : normalizedFile.sections) + if (section.segmentName == "__TEXT" && + section.sectionName == "__eh_frame") { + ehFrameSection = §ion; + break; + } + + // No __eh_frame so nothing to do. + if (!ehFrameSection) + return std::error_code(); + + file.eachAtomInSection(*ehFrameSection, + [&](MachODefinedAtom *atom, uint64_t offset) -> void { + assert(atom->contentType() == DefinedAtom::typeCFI); + + if (ArchHandler::isDwarfCIE(isBig, atom)) + return; + + // Compiler wasn't lazy and actually told us what it meant. + if (atom->begin() != atom->end()) + return; + + const uint8_t *frameData = atom->rawContent().data(); + uint32_t size = read32(frameData, isBig); + uint64_t cieFieldInFDE = size == 0xffffffffU + ? sizeof(uint32_t) + sizeof(uint64_t) + : sizeof(uint32_t); + + // Linker needs to fixup a reference from the FDE to its parent CIE (a + // 32-bit byte offset backwards in the __eh_frame section). + uint32_t cieDelta = read32(frameData + cieFieldInFDE, isBig); + uint64_t cieAddress = ehFrameSection->address + offset + cieFieldInFDE; + cieAddress -= cieDelta; + + Reference::Addend addend; + const Atom *cie = + findAtomCoveringAddress(normalizedFile, file, cieAddress, &addend); + atom->addReference(cieFieldInFDE, handler.unwindRefToCIEKind(), cie, + addend, handler.kindArch()); + + // Linker needs to fixup reference from the FDE to the function it's + // describing. FIXME: there are actually different ways to do this, and the + // particular method used is specified in the CIE's augmentation fields + // (hopefully) + uint64_t rangeFieldInFDE = cieFieldInFDE + sizeof(uint32_t); + + int64_t functionFromFDE = readSPtr(is64, isBig, frameData + rangeFieldInFDE); + uint64_t rangeStart = ehFrameSection->address + offset + rangeFieldInFDE; + rangeStart += functionFromFDE; + + const Atom *func = + findAtomCoveringAddress(normalizedFile, file, rangeStart, &addend); + atom->addReference(rangeFieldInFDE, handler.unwindRefToFunctionKind(), func, + addend, handler.kindArch()); + }); + return std::error_code(); +} + + +/// Converts normalized mach-o file into an lld::File and lld::Atoms. +ErrorOr<std::unique_ptr<lld::File>> +objectToAtoms(const NormalizedFile &normalizedFile, StringRef path, + bool copyRefs) { + std::unique_ptr<MachOFile> file(new MachOFile(path)); + if (std::error_code ec = normalizedObjectToAtoms( + file.get(), normalizedFile, copyRefs)) + return ec; + return std::unique_ptr<File>(std::move(file)); +} + +ErrorOr<std::unique_ptr<lld::File>> +dylibToAtoms(const NormalizedFile &normalizedFile, StringRef path, + bool copyRefs) { + // Instantiate SharedLibraryFile object. + std::unique_ptr<MachODylibFile> file(new MachODylibFile(path)); + normalizedDylibToAtoms(file.get(), normalizedFile, copyRefs); + return std::unique_ptr<File>(std::move(file)); +} + +} // anonymous namespace + +namespace normalized { + +std::error_code +normalizedObjectToAtoms(MachOFile *file, + const NormalizedFile &normalizedFile, + bool copyRefs) { + bool scatterable = ((normalizedFile.flags & MH_SUBSECTIONS_VIA_SYMBOLS) != 0); + + // Create atoms from each section. + for (auto § : normalizedFile.sections) { + if (isDebugInfoSection(sect)) + continue; + bool customSectionName; + DefinedAtom::ContentType atomType = atomTypeFromSection(sect, + customSectionName); + if (std::error_code ec = + processSection(atomType, sect, customSectionName, normalizedFile, + *file, scatterable, copyRefs)) + return ec; + } + // Create atoms from undefined symbols. + for (auto &sym : normalizedFile.undefinedSymbols) { + // Undefinded symbols with n_value != 0 are actually tentative definitions. + if (sym.value == Hex64(0)) { + file->addUndefinedAtom(sym.name, copyRefs); + } else { + file->addTentativeDefAtom(sym.name, atomScope(sym.scope), sym.value, + DefinedAtom::Alignment(sym.desc >> 8), copyRefs); + } + } + + // Convert mach-o relocations to References + std::unique_ptr<mach_o::ArchHandler> handler + = ArchHandler::create(normalizedFile.arch); + for (auto § : normalizedFile.sections) { + if (isDebugInfoSection(sect)) + continue; + if (std::error_code ec = convertRelocs(sect, normalizedFile, scatterable, + *file, *handler)) + return ec; + } + + // Add additional arch-specific References + file->eachDefinedAtom([&](MachODefinedAtom* atom) -> void { + handler->addAdditionalReferences(*atom); + }); + + // Each __eh_frame section needs references to both __text (the function we're + // providing unwind info for) and itself (FDE -> CIE). These aren't + // represented in the relocations on some architectures, so we have to add + // them back in manually there. + if (std::error_code ec = addEHFrameReferences(normalizedFile, *file, *handler)) + return ec; + + // Process mach-o data-in-code regions array. That information is encoded in + // atoms as References at each transition point. + unsigned nextIndex = 0; + for (const DataInCode &entry : normalizedFile.dataInCode) { + ++nextIndex; + const Section* s = findSectionCoveringAddress(normalizedFile, entry.offset); + if (!s) { + return make_dynamic_error_code(Twine("LC_DATA_IN_CODE address (" + + Twine(entry.offset) + + ") is not in any section")); + } + uint64_t offsetInSect = entry.offset - s->address; + uint32_t offsetInAtom; + MachODefinedAtom *atom = file->findAtomCoveringAddress(*s, offsetInSect, + &offsetInAtom); + if (offsetInAtom + entry.length > atom->size()) { + return make_dynamic_error_code(Twine("LC_DATA_IN_CODE entry (offset=" + + Twine(entry.offset) + + ", length=" + + Twine(entry.length) + + ") crosses atom boundary.")); + } + // Add reference that marks start of data-in-code. + atom->addReference(offsetInAtom, + handler->dataInCodeTransitionStart(*atom), atom, + entry.kind, handler->kindArch()); + + // Peek at next entry, if it starts where this one ends, skip ending ref. + if (nextIndex < normalizedFile.dataInCode.size()) { + const DataInCode &nextEntry = normalizedFile.dataInCode[nextIndex]; + if (nextEntry.offset == (entry.offset + entry.length)) + continue; + } + + // If data goes to end of function, skip ending ref. + if ((offsetInAtom + entry.length) == atom->size()) + continue; + + // Add reference that marks end of data-in-code. + atom->addReference(offsetInAtom+entry.length, + handler->dataInCodeTransitionEnd(*atom), atom, 0, + handler->kindArch()); + } + + // Sort references in each atom to their canonical order. + for (const DefinedAtom* defAtom : file->defined()) { + reinterpret_cast<const SimpleDefinedAtom*>(defAtom)->sortReferences(); + } + return std::error_code(); +} + +std::error_code +normalizedDylibToAtoms(MachODylibFile *file, + const NormalizedFile &normalizedFile, + bool copyRefs) { + file->setInstallName(normalizedFile.installName); + file->setCompatVersion(normalizedFile.compatVersion); + file->setCurrentVersion(normalizedFile.currentVersion); + + // Tell MachODylibFile object about all symbols it exports. + if (!normalizedFile.exportInfo.empty()) { + // If exports trie exists, use it instead of traditional symbol table. + for (const Export &exp : normalizedFile.exportInfo) { + bool weakDef = (exp.flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION); + // StringRefs from export iterator are ephemeral, so force copy. + file->addExportedSymbol(exp.name, weakDef, true); + } + } else { + for (auto &sym : normalizedFile.globalSymbols) { + assert((sym.scope & N_EXT) && "only expect external symbols here"); + bool weakDef = (sym.desc & N_WEAK_DEF); + file->addExportedSymbol(sym.name, weakDef, copyRefs); + } + } + // Tell MachODylibFile object about all dylibs it re-exports. + for (const DependentDylib &dep : normalizedFile.dependentDylibs) { + if (dep.kind == llvm::MachO::LC_REEXPORT_DYLIB) + file->addReExportedDylib(dep.path); + } + return std::error_code(); +} + +void relocatableSectionInfoForContentType(DefinedAtom::ContentType atomType, + StringRef &segmentName, + StringRef §ionName, + SectionType §ionType, + SectionAttr §ionAttrs) { + + for (const MachORelocatableSectionToAtomType *p = sectsToAtomType ; + p->atomType != DefinedAtom::typeUnknown; ++p) { + if (p->atomType != atomType) + continue; + // Wild carded entries are ignored for reverse lookups. + if (p->segmentName.empty() || p->sectionName.empty()) + continue; + segmentName = p->segmentName; + sectionName = p->sectionName; + sectionType = p->sectionType; + sectionAttrs = 0; + if (atomType == DefinedAtom::typeCode) + sectionAttrs = S_ATTR_PURE_INSTRUCTIONS; + return; + } + llvm_unreachable("content type not yet supported"); +} + +ErrorOr<std::unique_ptr<lld::File>> +normalizedToAtoms(const NormalizedFile &normalizedFile, StringRef path, + bool copyRefs) { + switch (normalizedFile.fileType) { + case MH_DYLIB: + case MH_DYLIB_STUB: + return dylibToAtoms(normalizedFile, path, copyRefs); + case MH_OBJECT: + return objectToAtoms(normalizedFile, path, copyRefs); + default: + llvm_unreachable("unhandled MachO file type!"); + } +} + +} // namespace normalized +} // namespace mach_o +} // namespace lld diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp new file mode 100644 index 0000000000000..ae14d755e2b9c --- /dev/null +++ b/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp @@ -0,0 +1,802 @@ +//===- lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp -----------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +/// +/// \file For mach-o object files, this implementation uses YAML I/O to +/// provide the convert between YAML and the normalized mach-o (NM). +/// +/// +------------+ +------+ +/// | normalized | <-> | yaml | +/// +------------+ +------+ + +#include "MachONormalizedFile.h" +#include "lld/Core/Error.h" +#include "lld/Core/LLVM.h" +#include "lld/ReaderWriter/YamlContext.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MachO.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" +#include <system_error> + + +using llvm::StringRef; +using namespace llvm::yaml; +using namespace llvm::MachO; +using namespace lld::mach_o::normalized; +using lld::YamlContext; + +LLVM_YAML_IS_SEQUENCE_VECTOR(Segment) +LLVM_YAML_IS_SEQUENCE_VECTOR(DependentDylib) +LLVM_YAML_IS_SEQUENCE_VECTOR(RebaseLocation) +LLVM_YAML_IS_SEQUENCE_VECTOR(BindLocation) +LLVM_YAML_IS_SEQUENCE_VECTOR(Export) +LLVM_YAML_IS_SEQUENCE_VECTOR(StringRef) +LLVM_YAML_IS_SEQUENCE_VECTOR(DataInCode) + + +// for compatibility with gcc-4.7 in C++11 mode, add extra namespace +namespace llvm { +namespace yaml { + +// A vector of Sections is a sequence. +template<> +struct SequenceTraits< std::vector<Section> > { + static size_t size(IO &io, std::vector<Section> &seq) { + return seq.size(); + } + static Section& element(IO &io, std::vector<Section> &seq, size_t index) { + if ( index >= seq.size() ) + seq.resize(index+1); + return seq[index]; + } +}; + +template<> +struct SequenceTraits< std::vector<Symbol> > { + static size_t size(IO &io, std::vector<Symbol> &seq) { + return seq.size(); + } + static Symbol& element(IO &io, std::vector<Symbol> &seq, size_t index) { + if ( index >= seq.size() ) + seq.resize(index+1); + return seq[index]; + } +}; + +// A vector of Relocations is a sequence. +template<> +struct SequenceTraits< Relocations > { + static size_t size(IO &io, Relocations &seq) { + return seq.size(); + } + static Relocation& element(IO &io, Relocations &seq, size_t index) { + if ( index >= seq.size() ) + seq.resize(index+1); + return seq[index]; + } +}; + +// The content for a section is represented as a flow sequence of hex bytes. +template<> +struct SequenceTraits< ContentBytes > { + static size_t size(IO &io, ContentBytes &seq) { + return seq.size(); + } + static Hex8& element(IO &io, ContentBytes &seq, size_t index) { + if ( index >= seq.size() ) + seq.resize(index+1); + return seq[index]; + } + static const bool flow = true; +}; + +// The indirect symbols for a section is represented as a flow sequence +// of numbers (symbol table indexes). +template<> +struct SequenceTraits< IndirectSymbols > { + static size_t size(IO &io, IndirectSymbols &seq) { + return seq.size(); + } + static uint32_t& element(IO &io, IndirectSymbols &seq, size_t index) { + if ( index >= seq.size() ) + seq.resize(index+1); + return seq[index]; + } + static const bool flow = true; +}; + +template <> +struct ScalarEnumerationTraits<lld::MachOLinkingContext::Arch> { + static void enumeration(IO &io, lld::MachOLinkingContext::Arch &value) { + io.enumCase(value, "unknown",lld::MachOLinkingContext::arch_unknown); + io.enumCase(value, "ppc", lld::MachOLinkingContext::arch_ppc); + io.enumCase(value, "x86", lld::MachOLinkingContext::arch_x86); + io.enumCase(value, "x86_64", lld::MachOLinkingContext::arch_x86_64); + io.enumCase(value, "armv6", lld::MachOLinkingContext::arch_armv6); + io.enumCase(value, "armv7", lld::MachOLinkingContext::arch_armv7); + io.enumCase(value, "armv7s", lld::MachOLinkingContext::arch_armv7s); + io.enumCase(value, "arm64", lld::MachOLinkingContext::arch_arm64); + } +}; + +template <> +struct ScalarEnumerationTraits<lld::MachOLinkingContext::OS> { + static void enumeration(IO &io, lld::MachOLinkingContext::OS &value) { + io.enumCase(value, "unknown", + lld::MachOLinkingContext::OS::unknown); + io.enumCase(value, "Mac OS X", + lld::MachOLinkingContext::OS::macOSX); + io.enumCase(value, "iOS", + lld::MachOLinkingContext::OS::iOS); + io.enumCase(value, "iOS Simulator", + lld::MachOLinkingContext::OS::iOS_simulator); + } +}; + + +template <> +struct ScalarEnumerationTraits<HeaderFileType> { + static void enumeration(IO &io, HeaderFileType &value) { + io.enumCase(value, "MH_OBJECT", llvm::MachO::MH_OBJECT); + io.enumCase(value, "MH_DYLIB", llvm::MachO::MH_DYLIB); + io.enumCase(value, "MH_EXECUTE", llvm::MachO::MH_EXECUTE); + io.enumCase(value, "MH_BUNDLE", llvm::MachO::MH_BUNDLE); + } +}; + + +template <> +struct ScalarBitSetTraits<FileFlags> { + static void bitset(IO &io, FileFlags &value) { + io.bitSetCase(value, "MH_TWOLEVEL", + llvm::MachO::MH_TWOLEVEL); + io.bitSetCase(value, "MH_SUBSECTIONS_VIA_SYMBOLS", + llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS); + } +}; + + +template <> +struct ScalarEnumerationTraits<SectionType> { + static void enumeration(IO &io, SectionType &value) { + io.enumCase(value, "S_REGULAR", + llvm::MachO::S_REGULAR); + io.enumCase(value, "S_ZEROFILL", + llvm::MachO::S_ZEROFILL); + io.enumCase(value, "S_CSTRING_LITERALS", + llvm::MachO::S_CSTRING_LITERALS); + io.enumCase(value, "S_4BYTE_LITERALS", + llvm::MachO::S_4BYTE_LITERALS); + io.enumCase(value, "S_8BYTE_LITERALS", + llvm::MachO::S_8BYTE_LITERALS); + io.enumCase(value, "S_LITERAL_POINTERS", + llvm::MachO::S_LITERAL_POINTERS); + io.enumCase(value, "S_NON_LAZY_SYMBOL_POINTERS", + llvm::MachO::S_NON_LAZY_SYMBOL_POINTERS); + io.enumCase(value, "S_LAZY_SYMBOL_POINTERS", + llvm::MachO::S_LAZY_SYMBOL_POINTERS); + io.enumCase(value, "S_SYMBOL_STUBS", + llvm::MachO::S_SYMBOL_STUBS); + io.enumCase(value, "S_MOD_INIT_FUNC_POINTERS", + llvm::MachO::S_MOD_INIT_FUNC_POINTERS); + io.enumCase(value, "S_MOD_TERM_FUNC_POINTERS", + llvm::MachO::S_MOD_TERM_FUNC_POINTERS); + io.enumCase(value, "S_COALESCED", + llvm::MachO::S_COALESCED); + io.enumCase(value, "S_GB_ZEROFILL", + llvm::MachO::S_GB_ZEROFILL); + io.enumCase(value, "S_INTERPOSING", + llvm::MachO::S_INTERPOSING); + io.enumCase(value, "S_16BYTE_LITERALS", + llvm::MachO::S_16BYTE_LITERALS); + io.enumCase(value, "S_DTRACE_DOF", + llvm::MachO::S_DTRACE_DOF); + io.enumCase(value, "S_LAZY_DYLIB_SYMBOL_POINTERS", + llvm::MachO::S_LAZY_DYLIB_SYMBOL_POINTERS); + io.enumCase(value, "S_THREAD_LOCAL_REGULAR", + llvm::MachO::S_THREAD_LOCAL_REGULAR); + io.enumCase(value, "S_THREAD_LOCAL_ZEROFILL", + llvm::MachO::S_THREAD_LOCAL_ZEROFILL); + io.enumCase(value, "S_THREAD_LOCAL_VARIABLES", + llvm::MachO::S_THREAD_LOCAL_VARIABLES); + io.enumCase(value, "S_THREAD_LOCAL_VARIABLE_POINTERS", + llvm::MachO::S_THREAD_LOCAL_VARIABLE_POINTERS); + io.enumCase(value, "S_THREAD_LOCAL_INIT_FUNCTION_POINTERS", + llvm::MachO::S_THREAD_LOCAL_INIT_FUNCTION_POINTERS); + } +}; + +template <> +struct ScalarBitSetTraits<SectionAttr> { + static void bitset(IO &io, SectionAttr &value) { + io.bitSetCase(value, "S_ATTR_PURE_INSTRUCTIONS", + llvm::MachO::S_ATTR_PURE_INSTRUCTIONS); + io.bitSetCase(value, "S_ATTR_SOME_INSTRUCTIONS", + llvm::MachO::S_ATTR_SOME_INSTRUCTIONS); + io.bitSetCase(value, "S_ATTR_NO_DEAD_STRIP", + llvm::MachO::S_ATTR_NO_DEAD_STRIP); + io.bitSetCase(value, "S_ATTR_EXT_RELOC", + llvm::MachO::S_ATTR_EXT_RELOC); + io.bitSetCase(value, "S_ATTR_LOC_RELOC", + llvm::MachO::S_ATTR_LOC_RELOC); + } +}; + +template <> +struct ScalarEnumerationTraits<NListType> { + static void enumeration(IO &io, NListType &value) { + io.enumCase(value, "N_UNDF", llvm::MachO::N_UNDF); + io.enumCase(value, "N_ABS", llvm::MachO::N_ABS); + io.enumCase(value, "N_SECT", llvm::MachO::N_SECT); + io.enumCase(value, "N_PBUD", llvm::MachO::N_PBUD); + io.enumCase(value, "N_INDR", llvm::MachO::N_INDR); + } +}; + +template <> +struct ScalarBitSetTraits<SymbolScope> { + static void bitset(IO &io, SymbolScope &value) { + io.bitSetCase(value, "N_EXT", llvm::MachO::N_EXT); + io.bitSetCase(value, "N_PEXT", llvm::MachO::N_PEXT); + } +}; + +template <> +struct ScalarBitSetTraits<SymbolDesc> { + static void bitset(IO &io, SymbolDesc &value) { + io.bitSetCase(value, "N_NO_DEAD_STRIP", llvm::MachO::N_NO_DEAD_STRIP); + io.bitSetCase(value, "N_WEAK_REF", llvm::MachO::N_WEAK_REF); + io.bitSetCase(value, "N_WEAK_DEF", llvm::MachO::N_WEAK_DEF); + io.bitSetCase(value, "N_ARM_THUMB_DEF", llvm::MachO::N_ARM_THUMB_DEF); + io.bitSetCase(value, "N_SYMBOL_RESOLVER", llvm::MachO::N_SYMBOL_RESOLVER); + } +}; + + +template <> +struct MappingTraits<Section> { + struct NormalizedContentBytes; + static void mapping(IO &io, Section §) { + io.mapRequired("segment", sect.segmentName); + io.mapRequired("section", sect.sectionName); + io.mapRequired("type", sect.type); + io.mapOptional("attributes", sect.attributes); + io.mapOptional("alignment", sect.alignment, 0U); + io.mapRequired("address", sect.address); + if (sect.type == llvm::MachO::S_ZEROFILL) { + // S_ZEROFILL sections use "size:" instead of "content:" + uint64_t size = sect.content.size(); + io.mapOptional("size", size); + if (!io.outputting()) { + uint8_t *bytes = nullptr; + sect.content = makeArrayRef(bytes, size); + } + } else { + MappingNormalization<NormalizedContent, ArrayRef<uint8_t>> content( + io, sect.content); + io.mapOptional("content", content->_normalizedContent); + } + io.mapOptional("relocations", sect.relocations); + io.mapOptional("indirect-syms", sect.indirectSymbols); + } + + struct NormalizedContent { + NormalizedContent(IO &io) : _io(io) {} + NormalizedContent(IO &io, ArrayRef<uint8_t> content) : _io(io) { + // When writing yaml, copy content byte array to Hex8 vector. + for (auto &c : content) { + _normalizedContent.push_back(c); + } + } + ArrayRef<uint8_t> denormalize(IO &io) { + // When reading yaml, allocate byte array owned by NormalizedFile and + // copy Hex8 vector to byte array. + YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); + assert(info != nullptr); + NormalizedFile *file = info->_normalizeMachOFile; + assert(file != nullptr); + size_t size = _normalizedContent.size(); + uint8_t *bytes = file->ownedAllocations.Allocate<uint8_t>(size); + std::copy(_normalizedContent.begin(), _normalizedContent.end(), bytes); + return makeArrayRef(bytes, size); + } + + IO &_io; + ContentBytes _normalizedContent; + }; +}; + + +template <> +struct MappingTraits<Relocation> { + static void mapping(IO &io, Relocation &reloc) { + io.mapRequired("offset", reloc.offset); + io.mapOptional("scattered", reloc.scattered, false); + io.mapRequired("type", reloc.type); + io.mapRequired("length", reloc.length); + io.mapRequired("pc-rel", reloc.pcRel); + if ( !reloc.scattered ) + io.mapRequired("extern", reloc.isExtern); + if ( reloc.scattered ) + io.mapRequired("value", reloc.value); + if ( !reloc.scattered ) + io.mapRequired("symbol", reloc.symbol); + } +}; + + +template <> +struct ScalarEnumerationTraits<RelocationInfoType> { + static void enumeration(IO &io, RelocationInfoType &value) { + YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); + assert(info != nullptr); + NormalizedFile *file = info->_normalizeMachOFile; + assert(file != nullptr); + switch (file->arch) { + case lld::MachOLinkingContext::arch_x86_64: + io.enumCase(value, "X86_64_RELOC_UNSIGNED", + llvm::MachO::X86_64_RELOC_UNSIGNED); + io.enumCase(value, "X86_64_RELOC_SIGNED", + llvm::MachO::X86_64_RELOC_SIGNED); + io.enumCase(value, "X86_64_RELOC_BRANCH", + llvm::MachO::X86_64_RELOC_BRANCH); + io.enumCase(value, "X86_64_RELOC_GOT_LOAD", + llvm::MachO::X86_64_RELOC_GOT_LOAD); + io.enumCase(value, "X86_64_RELOC_GOT", + llvm::MachO::X86_64_RELOC_GOT); + io.enumCase(value, "X86_64_RELOC_SUBTRACTOR", + llvm::MachO::X86_64_RELOC_SUBTRACTOR); + io.enumCase(value, "X86_64_RELOC_SIGNED_1", + llvm::MachO::X86_64_RELOC_SIGNED_1); + io.enumCase(value, "X86_64_RELOC_SIGNED_2", + llvm::MachO::X86_64_RELOC_SIGNED_2); + io.enumCase(value, "X86_64_RELOC_SIGNED_4", + llvm::MachO::X86_64_RELOC_SIGNED_4); + io.enumCase(value, "X86_64_RELOC_TLV", + llvm::MachO::X86_64_RELOC_TLV); + break; + case lld::MachOLinkingContext::arch_x86: + io.enumCase(value, "GENERIC_RELOC_VANILLA", + llvm::MachO::GENERIC_RELOC_VANILLA); + io.enumCase(value, "GENERIC_RELOC_PAIR", + llvm::MachO::GENERIC_RELOC_PAIR); + io.enumCase(value, "GENERIC_RELOC_SECTDIFF", + llvm::MachO::GENERIC_RELOC_SECTDIFF); + io.enumCase(value, "GENERIC_RELOC_LOCAL_SECTDIFF", + llvm::MachO::GENERIC_RELOC_LOCAL_SECTDIFF); + io.enumCase(value, "GENERIC_RELOC_TLV", + llvm::MachO::GENERIC_RELOC_TLV); + break; + case lld::MachOLinkingContext::arch_armv6: + case lld::MachOLinkingContext::arch_armv7: + case lld::MachOLinkingContext::arch_armv7s: + io.enumCase(value, "ARM_RELOC_VANILLA", + llvm::MachO::ARM_RELOC_VANILLA); + io.enumCase(value, "ARM_RELOC_PAIR", + llvm::MachO::ARM_RELOC_PAIR); + io.enumCase(value, "ARM_RELOC_SECTDIFF", + llvm::MachO::ARM_RELOC_SECTDIFF); + io.enumCase(value, "ARM_RELOC_LOCAL_SECTDIFF", + llvm::MachO::ARM_RELOC_LOCAL_SECTDIFF); + io.enumCase(value, "ARM_RELOC_BR24", + llvm::MachO::ARM_RELOC_BR24); + io.enumCase(value, "ARM_THUMB_RELOC_BR22", + llvm::MachO::ARM_THUMB_RELOC_BR22); + io.enumCase(value, "ARM_RELOC_HALF", + llvm::MachO::ARM_RELOC_HALF); + io.enumCase(value, "ARM_RELOC_HALF_SECTDIFF", + llvm::MachO::ARM_RELOC_HALF_SECTDIFF); + break; + case lld::MachOLinkingContext::arch_arm64: + io.enumCase(value, "ARM64_RELOC_UNSIGNED", + llvm::MachO::ARM64_RELOC_UNSIGNED); + io.enumCase(value, "ARM64_RELOC_SUBTRACTOR", + llvm::MachO::ARM64_RELOC_SUBTRACTOR); + io.enumCase(value, "ARM64_RELOC_BRANCH26", + llvm::MachO::ARM64_RELOC_BRANCH26); + io.enumCase(value, "ARM64_RELOC_PAGE21", + llvm::MachO::ARM64_RELOC_PAGE21); + io.enumCase(value, "ARM64_RELOC_PAGEOFF12", + llvm::MachO::ARM64_RELOC_PAGEOFF12); + io.enumCase(value, "ARM64_RELOC_GOT_LOAD_PAGE21", + llvm::MachO::ARM64_RELOC_GOT_LOAD_PAGE21); + io.enumCase(value, "ARM64_RELOC_GOT_LOAD_PAGEOFF12", + llvm::MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12); + io.enumCase(value, "ARM64_RELOC_POINTER_TO_GOT", + llvm::MachO::ARM64_RELOC_POINTER_TO_GOT); + io.enumCase(value, "ARM64_RELOC_TLVP_LOAD_PAGE21", + llvm::MachO::ARM64_RELOC_TLVP_LOAD_PAGE21); + io.enumCase(value, "ARM64_RELOC_TLVP_LOAD_PAGEOFF12", + llvm::MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12); + io.enumCase(value, "ARM64_RELOC_ADDEND", + llvm::MachO::ARM64_RELOC_ADDEND); + break; + default: + llvm_unreachable("unknown architecture"); + } + } +}; + + +template <> +struct MappingTraits<Symbol> { + static void mapping(IO &io, Symbol& sym) { + io.mapRequired("name", sym.name); + io.mapRequired("type", sym.type); + io.mapOptional("scope", sym.scope, SymbolScope(0)); + io.mapOptional("sect", sym.sect, (uint8_t)0); + if (sym.type == llvm::MachO::N_UNDF) { + // In undef symbols, desc field contains alignment/ordinal info + // which is better represented as a hex vaule. + uint16_t t1 = sym.desc; + Hex16 t2 = t1; + io.mapOptional("desc", t2, Hex16(0)); + sym.desc = t2; + } else { + // In defined symbols, desc fit is a set of option bits. + io.mapOptional("desc", sym.desc, SymbolDesc(0)); + } + io.mapRequired("value", sym.value); + } +}; + +// Custom mapping for VMProtect (e.g. "r-x"). +template <> +struct ScalarTraits<VMProtect> { + static void output(const VMProtect &value, void*, raw_ostream &out) { + out << ( (value & llvm::MachO::VM_PROT_READ) ? 'r' : '-'); + out << ( (value & llvm::MachO::VM_PROT_WRITE) ? 'w' : '-'); + out << ( (value & llvm::MachO::VM_PROT_EXECUTE) ? 'x' : '-'); + } + static StringRef input(StringRef scalar, void*, VMProtect &value) { + value = 0; + if (scalar.size() != 3) + return "segment access protection must be three chars (e.g. \"r-x\")"; + switch (scalar[0]) { + case 'r': + value = llvm::MachO::VM_PROT_READ; + break; + case '-': + break; + default: + return "segment access protection first char must be 'r' or '-'"; + } + switch (scalar[1]) { + case 'w': + value = value | llvm::MachO::VM_PROT_WRITE; + break; + case '-': + break; + default: + return "segment access protection second char must be 'w' or '-'"; + } + switch (scalar[2]) { + case 'x': + value = value | llvm::MachO::VM_PROT_EXECUTE; + break; + case '-': + break; + default: + return "segment access protection third char must be 'x' or '-'"; + } + // Return the empty string on success, + return StringRef(); + } + static bool mustQuote(StringRef) { return false; } +}; + + +template <> +struct MappingTraits<Segment> { + static void mapping(IO &io, Segment& seg) { + io.mapRequired("name", seg.name); + io.mapRequired("address", seg.address); + io.mapRequired("size", seg.size); + io.mapRequired("access", seg.access); + } +}; + +template <> +struct ScalarEnumerationTraits<LoadCommandType> { + static void enumeration(IO &io, LoadCommandType &value) { + io.enumCase(value, "LC_LOAD_DYLIB", + llvm::MachO::LC_LOAD_DYLIB); + io.enumCase(value, "LC_LOAD_WEAK_DYLIB", + llvm::MachO::LC_LOAD_WEAK_DYLIB); + io.enumCase(value, "LC_REEXPORT_DYLIB", + llvm::MachO::LC_REEXPORT_DYLIB); + io.enumCase(value, "LC_LOAD_UPWARD_DYLIB", + llvm::MachO::LC_LOAD_UPWARD_DYLIB); + io.enumCase(value, "LC_LAZY_LOAD_DYLIB", + llvm::MachO::LC_LAZY_LOAD_DYLIB); + } +}; + +template <> +struct MappingTraits<DependentDylib> { + static void mapping(IO &io, DependentDylib& dylib) { + io.mapRequired("path", dylib.path); + io.mapOptional("kind", dylib.kind, + llvm::MachO::LC_LOAD_DYLIB); + io.mapOptional("compat-version", dylib.compatVersion, + PackedVersion(0x10000)); + io.mapOptional("current-version", dylib.currentVersion, + PackedVersion(0x10000)); + } +}; + +template <> +struct ScalarEnumerationTraits<RebaseType> { + static void enumeration(IO &io, RebaseType &value) { + io.enumCase(value, "REBASE_TYPE_POINTER", + llvm::MachO::REBASE_TYPE_POINTER); + io.enumCase(value, "REBASE_TYPE_TEXT_PCREL32", + llvm::MachO::REBASE_TYPE_TEXT_PCREL32); + io.enumCase(value, "REBASE_TYPE_TEXT_ABSOLUTE32", + llvm::MachO::REBASE_TYPE_TEXT_ABSOLUTE32); + } +}; + + +template <> +struct MappingTraits<RebaseLocation> { + static void mapping(IO &io, RebaseLocation& rebase) { + io.mapRequired("segment-index", rebase.segIndex); + io.mapRequired("segment-offset", rebase.segOffset); + io.mapOptional("kind", rebase.kind, + llvm::MachO::REBASE_TYPE_POINTER); + } +}; + + + +template <> +struct ScalarEnumerationTraits<BindType> { + static void enumeration(IO &io, BindType &value) { + io.enumCase(value, "BIND_TYPE_POINTER", + llvm::MachO::BIND_TYPE_POINTER); + io.enumCase(value, "BIND_TYPE_TEXT_ABSOLUTE32", + llvm::MachO::BIND_TYPE_TEXT_ABSOLUTE32); + io.enumCase(value, "BIND_TYPE_TEXT_PCREL32", + llvm::MachO::BIND_TYPE_TEXT_PCREL32); + } +}; + +template <> +struct MappingTraits<BindLocation> { + static void mapping(IO &io, BindLocation &bind) { + io.mapRequired("segment-index", bind.segIndex); + io.mapRequired("segment-offset", bind.segOffset); + io.mapOptional("kind", bind.kind, + llvm::MachO::BIND_TYPE_POINTER); + io.mapOptional("can-be-null", bind.canBeNull, false); + io.mapRequired("ordinal", bind.ordinal); + io.mapRequired("symbol-name", bind.symbolName); + io.mapOptional("addend", bind.addend, Hex64(0)); + } +}; + + +template <> +struct ScalarEnumerationTraits<ExportSymbolKind> { + static void enumeration(IO &io, ExportSymbolKind &value) { + io.enumCase(value, "EXPORT_SYMBOL_FLAGS_KIND_REGULAR", + llvm::MachO::EXPORT_SYMBOL_FLAGS_KIND_REGULAR); + io.enumCase(value, "EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL", + llvm::MachO::EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL); + io.enumCase(value, "EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE", + llvm::MachO::EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE); + } +}; + +template <> +struct ScalarBitSetTraits<ExportFlags> { + static void bitset(IO &io, ExportFlags &value) { + io.bitSetCase(value, "EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION", + llvm::MachO::EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION); + io.bitSetCase(value, "EXPORT_SYMBOL_FLAGS_REEXPORT", + llvm::MachO::EXPORT_SYMBOL_FLAGS_REEXPORT); + io.bitSetCase(value, "EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER", + llvm::MachO::EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER); + } +}; + + +template <> +struct MappingTraits<Export> { + static void mapping(IO &io, Export &exp) { + io.mapRequired("name", exp.name); + io.mapOptional("offset", exp.offset); + io.mapOptional("kind", exp.kind, + llvm::MachO::EXPORT_SYMBOL_FLAGS_KIND_REGULAR); + if (!io.outputting() || exp.flags) + io.mapOptional("flags", exp.flags); + io.mapOptional("other", exp.otherOffset, Hex32(0)); + io.mapOptional("other-name", exp.otherName, StringRef()); + } +}; + +template <> +struct ScalarEnumerationTraits<DataRegionType> { + static void enumeration(IO &io, DataRegionType &value) { + io.enumCase(value, "DICE_KIND_DATA", + llvm::MachO::DICE_KIND_DATA); + io.enumCase(value, "DICE_KIND_JUMP_TABLE8", + llvm::MachO::DICE_KIND_JUMP_TABLE8); + io.enumCase(value, "DICE_KIND_JUMP_TABLE16", + llvm::MachO::DICE_KIND_JUMP_TABLE16); + io.enumCase(value, "DICE_KIND_JUMP_TABLE32", + llvm::MachO::DICE_KIND_JUMP_TABLE32); + io.enumCase(value, "DICE_KIND_ABS_JUMP_TABLE32", + llvm::MachO::DICE_KIND_ABS_JUMP_TABLE32); + } +}; + +template <> +struct MappingTraits<DataInCode> { + static void mapping(IO &io, DataInCode &entry) { + io.mapRequired("offset", entry.offset); + io.mapRequired("length", entry.length); + io.mapRequired("kind", entry.kind); + } +}; + +template <> +struct ScalarTraits<PackedVersion> { + static void output(const PackedVersion &value, void*, raw_ostream &out) { + out << llvm::format("%d.%d", (value >> 16), (value >> 8) & 0xFF); + if (value & 0xFF) { + out << llvm::format(".%d", (value & 0xFF)); + } + } + static StringRef input(StringRef scalar, void*, PackedVersion &result) { + uint32_t value; + if (lld::MachOLinkingContext::parsePackedVersion(scalar, value)) + return "malformed version number"; + result = value; + // Return the empty string on success, + return StringRef(); + } + static bool mustQuote(StringRef) { return false; } +}; + +template <> +struct MappingTraits<NormalizedFile> { + static void mapping(IO &io, NormalizedFile &file) { + io.mapRequired("arch", file.arch); + io.mapRequired("file-type", file.fileType); + io.mapOptional("flags", file.flags); + io.mapOptional("dependents", file.dependentDylibs); + io.mapOptional("install-name", file.installName, StringRef()); + io.mapOptional("compat-version", file.compatVersion, PackedVersion(0x10000)); + io.mapOptional("current-version", file.currentVersion, PackedVersion(0x10000)); + io.mapOptional("has-UUID", file.hasUUID, true); + io.mapOptional("rpaths", file.rpaths); + io.mapOptional("entry-point", file.entryAddress, Hex64(0)); + io.mapOptional("source-version", file.sourceVersion, Hex64(0)); + io.mapOptional("OS", file.os); + io.mapOptional("min-os-version", file.minOSverson, PackedVersion(0)); + io.mapOptional("sdk-version", file.sdkVersion, PackedVersion(0)); + io.mapOptional("segments", file.segments); + io.mapOptional("sections", file.sections); + io.mapOptional("local-symbols", file.localSymbols); + io.mapOptional("global-symbols", file.globalSymbols); + io.mapOptional("undefined-symbols",file.undefinedSymbols); + io.mapOptional("page-size", file.pageSize, Hex32(4096)); + io.mapOptional("rebasings", file.rebasingInfo); + io.mapOptional("bindings", file.bindingInfo); + io.mapOptional("weak-bindings", file.weakBindingInfo); + io.mapOptional("lazy-bindings", file.lazyBindingInfo); + io.mapOptional("exports", file.exportInfo); + io.mapOptional("dataInCode", file.dataInCode); + } + static StringRef validate(IO &io, NormalizedFile &file) { + return StringRef(); + } +}; + +} // namespace llvm +} // namespace yaml + + +namespace lld { +namespace mach_o { + +/// Handles !mach-o tagged yaml documents. +bool MachOYamlIOTaggedDocumentHandler::handledDocTag(llvm::yaml::IO &io, + const lld::File *&file) const { + if (!io.mapTag("!mach-o")) + return false; + // Step 1: parse yaml into normalized mach-o struct. + NormalizedFile nf; + YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); + assert(info != nullptr); + assert(info->_normalizeMachOFile == nullptr); + info->_normalizeMachOFile = &nf; + MappingTraits<NormalizedFile>::mapping(io, nf); + // Step 2: parse normalized mach-o struct into atoms. + ErrorOr<std::unique_ptr<lld::File>> foe = normalizedToAtoms(nf, info->_path, + true); + if (nf.arch != _arch) { + io.setError(Twine("file is wrong architecture. Expected (" + + MachOLinkingContext::nameFromArch(_arch) + + ") found (" + + MachOLinkingContext::nameFromArch(nf.arch) + + ")")); + return false; + } + info->_normalizeMachOFile = nullptr; + + if (foe) { + // Transfer ownership to "out" File parameter. + std::unique_ptr<lld::File> f = std::move(foe.get()); + file = f.release(); + return true; + } else { + io.setError(foe.getError().message()); + return false; + } +} + + + +namespace normalized { + +/// Parses a yaml encoded mach-o file to produce an in-memory normalized view. +ErrorOr<std::unique_ptr<NormalizedFile>> +readYaml(std::unique_ptr<MemoryBuffer> &mb) { + // Make empty NormalizedFile. + std::unique_ptr<NormalizedFile> f(new NormalizedFile()); + + // Create YAML Input parser. + YamlContext yamlContext; + yamlContext._normalizeMachOFile = f.get(); + llvm::yaml::Input yin(mb->getBuffer(), &yamlContext); + + // Fill NormalizedFile by parsing yaml. + yin >> *f; + + // Return error if there were parsing problems. + if (yin.error()) + return make_error_code(lld::YamlReaderError::illegal_value); + + // Hand ownership of instantiated NormalizedFile to caller. + return std::move(f); +} + + +/// Writes a yaml encoded mach-o files from an in-memory normalized view. +std::error_code writeYaml(const NormalizedFile &file, raw_ostream &out) { + // YAML I/O is not const aware, so need to cast away ;-( + NormalizedFile *f = const_cast<NormalizedFile*>(&file); + + // Create yaml Output writer, using yaml options for context. + YamlContext yamlContext; + yamlContext._normalizeMachOFile = f; + llvm::yaml::Output yout(out, &yamlContext); + + // Stream out yaml. + yout << *f; + + return std::error_code(); +} + +} // namespace normalized +} // namespace mach_o +} // namespace lld + diff --git a/lib/ReaderWriter/MachO/MachOPasses.h b/lib/ReaderWriter/MachO/MachOPasses.h new file mode 100644 index 0000000000000..86f4bc0f5d54c --- /dev/null +++ b/lib/ReaderWriter/MachO/MachOPasses.h @@ -0,0 +1,28 @@ +//===- lib/ReaderWriter/MachO/MachOPasses.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_PASSES_H +#define LLD_READER_WRITER_MACHO_PASSES_H + +#include "lld/Core/PassManager.h" +#include "lld/ReaderWriter/MachOLinkingContext.h" + +namespace lld { +namespace mach_o { + +void addLayoutPass(PassManager &pm, const MachOLinkingContext &ctx); +void addStubsPass(PassManager &pm, const MachOLinkingContext &ctx); +void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx); +void addCompactUnwindPass(PassManager &pm, const MachOLinkingContext &ctx); +void addShimPass(PassManager &pm, const MachOLinkingContext &ctx); + +} // namespace mach_o +} // namespace lld + +#endif // LLD_READER_WRITER_MACHO_PASSES_H diff --git a/lib/ReaderWriter/MachO/Makefile b/lib/ReaderWriter/MachO/Makefile new file mode 100644 index 0000000000000..1acd578ba9d34 --- /dev/null +++ b/lib/ReaderWriter/MachO/Makefile @@ -0,0 +1,14 @@ +##===- lld/lib/ReaderWriter/MachO/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 := lldMachO +USEDLIBS = lldCore.a + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/MachO/ShimPass.cpp b/lib/ReaderWriter/MachO/ShimPass.cpp new file mode 100644 index 0000000000000..a8c69f8ceace8 --- /dev/null +++ b/lib/ReaderWriter/MachO/ShimPass.cpp @@ -0,0 +1,129 @@ +//===- lib/ReaderWriter/MachO/ShimPass.cpp -------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This linker pass updates branch-sites whose target is a different mode +// (thumb vs arm). +// +// Arm code has two instruction encodings thumb and arm. When branching from +// one code encoding to another, you need to use an instruction that switches +// the instruction mode. Usually the transition only happens at call sites, and +// the linker can transform a BL instruction in BLX (or vice versa). But if the +// compiler did a tail call optimization and a function ends with a branch (not +// branch and link), there is no pc-rel BX instruction. +// +// The ShimPass looks for pc-rel B instructions that will need to switch mode. +// For those cases it synthesizes a shim which does the transition, then +// modifies the original atom with the B instruction to target to the shim atom. +// +//===----------------------------------------------------------------------===// + +#include "ArchHandler.h" +#include "File.h" +#include "MachOPasses.h" +#include "lld/Core/DefinedAtom.h" +#include "lld/Core/File.h" +#include "lld/Core/LLVM.h" +#include "lld/Core/Reference.h" +#include "lld/Core/Simple.h" +#include "lld/ReaderWriter/MachOLinkingContext.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" + +namespace lld { +namespace mach_o { + +class ShimPass : public Pass { +public: + ShimPass(const MachOLinkingContext &context) + : _context(context) + , _archHandler(_context.archHandler()) + , _stubInfo(_archHandler.stubInfo()) + , _file("<mach-o shim pass>") { + } + + + void perform(std::unique_ptr<MutableFile> &mergedFile) override { + // Scan all references in all atoms. + for (const DefinedAtom *atom : mergedFile->defined()) { + for (const Reference *ref : *atom) { + // Look at non-call branches. + if (!_archHandler.isNonCallBranch(*ref)) + continue; + const Atom *target = ref->target(); + assert(target != nullptr); + if (const lld::DefinedAtom *daTarget = dyn_cast<DefinedAtom>(target)) { + bool atomIsThumb = _archHandler.isThumbFunction(*atom); + bool targetIsThumb = _archHandler.isThumbFunction(*daTarget); + if (atomIsThumb != targetIsThumb) + updateBranchToUseShim(atomIsThumb, *daTarget, ref); + } + } + } + // Exit early if no shims needed. + if (_targetToShim.empty()) + return; + + // Sort shim atoms so the layout order is stable. + std::vector<const DefinedAtom *> shims; + shims.reserve(_targetToShim.size()); + for (auto element : _targetToShim) { + shims.push_back(element.second); + } + std::sort(shims.begin(), shims.end(), + [](const DefinedAtom *l, const DefinedAtom *r) { + return (l->name() < r->name()); + }); + + // Add all shims to master file. + for (const DefinedAtom *shim : shims) { + mergedFile->addAtom(*shim); + } + } + +private: + + void updateBranchToUseShim(bool thumbToArm, const DefinedAtom& target, + const Reference *ref) { + // Make file-format specific stub and other support atoms. + const DefinedAtom *shim = this->getShim(thumbToArm, target); + assert(shim != nullptr); + // Switch branch site to target shim atom. + const_cast<Reference *>(ref)->setTarget(shim); + } + + const DefinedAtom* getShim(bool thumbToArm, const DefinedAtom& target) { + auto pos = _targetToShim.find(&target); + if ( pos != _targetToShim.end() ) { + // Reuse an existing shim. + assert(pos->second != nullptr); + return pos->second; + } else { + // There is no existing shim, so create a new one. + const DefinedAtom *shim = _archHandler.createShim(_file, thumbToArm, + target); + _targetToShim[&target] = shim; + return shim; + } + } + + const MachOLinkingContext &_context; + mach_o::ArchHandler &_archHandler; + const ArchHandler::StubInfo &_stubInfo; + MachOFile _file; + llvm::DenseMap<const Atom*, const DefinedAtom*> _targetToShim; +}; + + + +void addShimPass(PassManager &pm, const MachOLinkingContext &ctx) { + pm.add(llvm::make_unique<ShimPass>(ctx)); +} + +} // end namespace mach_o +} // end namespace lld diff --git a/lib/ReaderWriter/MachO/StubsPass.cpp b/lib/ReaderWriter/MachO/StubsPass.cpp new file mode 100644 index 0000000000000..bc4d9c2087f3e --- /dev/null +++ b/lib/ReaderWriter/MachO/StubsPass.cpp @@ -0,0 +1,373 @@ +//===- lib/ReaderWriter/MachO/StubsPass.cpp -------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This linker pass updates call-sites which have references to shared library +// atoms to instead have a reference to a stub (PLT entry) for the specified +// symbol. Each file format defines a subclass of StubsPass which implements +// the abstract methods for creating the file format specific StubAtoms. +// +//===----------------------------------------------------------------------===// + +#include "ArchHandler.h" +#include "File.h" +#include "MachOPasses.h" +#include "lld/Core/DefinedAtom.h" +#include "lld/Core/File.h" +#include "lld/Core/LLVM.h" +#include "lld/Core/Reference.h" +#include "lld/Core/Simple.h" +#include "lld/ReaderWriter/MachOLinkingContext.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" + + +namespace lld { +namespace mach_o { + + +// +// Lazy Pointer Atom created by the stubs pass. +// +class LazyPointerAtom : public SimpleDefinedAtom { +public: + LazyPointerAtom(const File &file, bool is64) + : SimpleDefinedAtom(file), _is64(is64) { } + + ContentType contentType() const override { + return DefinedAtom::typeLazyPointer; + } + + Alignment alignment() const override { + return Alignment(_is64 ? 3 : 2); + } + + uint64_t size() const override { + return _is64 ? 8 : 4; + } + + ContentPermissions permissions() const override { + return DefinedAtom::permRW_; + } + + ArrayRef<uint8_t> rawContent() const override { + static const uint8_t zeros[] = + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + return llvm::makeArrayRef(zeros, size()); + } + +private: + const bool _is64; +}; + + +// +// NonLazyPointer (GOT) Atom created by the stubs pass. +// +class NonLazyPointerAtom : public SimpleDefinedAtom { +public: + NonLazyPointerAtom(const File &file, bool is64) + : SimpleDefinedAtom(file), _is64(is64) { } + + ContentType contentType() const override { + return DefinedAtom::typeGOT; + } + + Alignment alignment() const override { + return Alignment(_is64 ? 3 : 2); + } + + uint64_t size() const override { + return _is64 ? 8 : 4; + } + + ContentPermissions permissions() const override { + return DefinedAtom::permRW_; + } + + ArrayRef<uint8_t> rawContent() const override { + static const uint8_t zeros[] = + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + return llvm::makeArrayRef(zeros, size()); + } + +private: + const bool _is64; +}; + + + +// +// Stub Atom created by the stubs pass. +// +class StubAtom : public SimpleDefinedAtom { +public: + StubAtom(const File &file, const ArchHandler::StubInfo &stubInfo) + : SimpleDefinedAtom(file), _stubInfo(stubInfo){ } + + ContentType contentType() const override { + return DefinedAtom::typeStub; + } + + Alignment alignment() const override { + return Alignment(_stubInfo.codeAlignment); + } + + uint64_t size() const override { + return _stubInfo.stubSize; + } + + ContentPermissions permissions() const override { + return DefinedAtom::permR_X; + } + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(_stubInfo.stubBytes, _stubInfo.stubSize); + } + +private: + const ArchHandler::StubInfo &_stubInfo; +}; + + +// +// Stub Helper Atom created by the stubs pass. +// +class StubHelperAtom : public SimpleDefinedAtom { +public: + StubHelperAtom(const File &file, const ArchHandler::StubInfo &stubInfo) + : SimpleDefinedAtom(file), _stubInfo(stubInfo) { } + + ContentType contentType() const override { + return DefinedAtom::typeStubHelper; + } + + Alignment alignment() const override { + return Alignment(_stubInfo.codeAlignment); + } + + uint64_t size() const override { + return _stubInfo.stubHelperSize; + } + + ContentPermissions permissions() const override { + return DefinedAtom::permR_X; + } + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(_stubInfo.stubHelperBytes, + _stubInfo.stubHelperSize); + } + +private: + const ArchHandler::StubInfo &_stubInfo; +}; + + +// +// Stub Helper Common Atom created by the stubs pass. +// +class StubHelperCommonAtom : public SimpleDefinedAtom { +public: + StubHelperCommonAtom(const File &file, const ArchHandler::StubInfo &stubInfo) + : SimpleDefinedAtom(file), _stubInfo(stubInfo) { } + + ContentType contentType() const override { + return DefinedAtom::typeStubHelper; + } + + Alignment alignment() const override { + return Alignment(_stubInfo.codeAlignment); + } + + uint64_t size() const override { + return _stubInfo.stubHelperCommonSize; + } + + ContentPermissions permissions() const override { + return DefinedAtom::permR_X; + } + + ArrayRef<uint8_t> rawContent() const override { + return llvm::makeArrayRef(_stubInfo.stubHelperCommonBytes, + _stubInfo.stubHelperCommonSize); + } + +private: + const ArchHandler::StubInfo &_stubInfo; +}; + + +class StubsPass : public Pass { +public: + StubsPass(const MachOLinkingContext &context) + : _context(context), _archHandler(_context.archHandler()), + _stubInfo(_archHandler.stubInfo()), _file("<mach-o Stubs pass>") { } + + + void perform(std::unique_ptr<MutableFile> &mergedFile) override { + // Skip this pass if output format uses text relocations instead of stubs. + if (!this->noTextRelocs()) + return; + + // Scan all references in all atoms. + for (const DefinedAtom *atom : mergedFile->defined()) { + for (const Reference *ref : *atom) { + // Look at call-sites. + if (!this->isCallSite(*ref)) + continue; + const Atom *target = ref->target(); + assert(target != nullptr); + if (isa<SharedLibraryAtom>(target)) { + // Calls to shared libraries go through stubs. + _targetToUses[target].push_back(ref); + continue; + } + const DefinedAtom *defTarget = dyn_cast<DefinedAtom>(target); + if (defTarget && defTarget->interposable() != DefinedAtom::interposeNo){ + // Calls to interposable functions in same linkage unit must also go + // through a stub. + assert(defTarget->scope() != DefinedAtom::scopeTranslationUnit); + _targetToUses[target].push_back(ref); + } + } + } + + // Exit early if no stubs needed. + if (_targetToUses.empty()) + return; + + // First add help-common and GOT slots used by lazy binding. + SimpleDefinedAtom *helperCommonAtom = + new (_file.allocator()) StubHelperCommonAtom(_file, _stubInfo); + SimpleDefinedAtom *helperCacheNLPAtom = + new (_file.allocator()) NonLazyPointerAtom(_file, _context.is64Bit()); + SimpleDefinedAtom *helperBinderNLPAtom = + new (_file.allocator()) NonLazyPointerAtom(_file, _context.is64Bit()); + addReference(helperCommonAtom, _stubInfo.stubHelperCommonReferenceToCache, + helperCacheNLPAtom); + addOptReference( + helperCommonAtom, _stubInfo.stubHelperCommonReferenceToCache, + _stubInfo.optStubHelperCommonReferenceToCache, helperCacheNLPAtom); + addReference(helperCommonAtom, _stubInfo.stubHelperCommonReferenceToBinder, + helperBinderNLPAtom); + addOptReference( + helperCommonAtom, _stubInfo.stubHelperCommonReferenceToBinder, + _stubInfo.optStubHelperCommonReferenceToBinder, helperBinderNLPAtom); + mergedFile->addAtom(*helperCommonAtom); + mergedFile->addAtom(*helperBinderNLPAtom); + mergedFile->addAtom(*helperCacheNLPAtom); + + // Add reference to dyld_stub_binder in libSystem.dylib + auto I = std::find_if( + mergedFile->sharedLibrary().begin(), mergedFile->sharedLibrary().end(), + [&](const SharedLibraryAtom *atom) { + return atom->name().equals(_stubInfo.binderSymbolName); + }); + assert(I != mergedFile->sharedLibrary().end() && "dyld_stub_binder not found"); + addReference(helperBinderNLPAtom, _stubInfo.nonLazyPointerReferenceToBinder, *I); + + // Sort targets by name, so stubs and lazy pointers are consistent + std::vector<const Atom *> targetsNeedingStubs; + for (auto it : _targetToUses) + targetsNeedingStubs.push_back(it.first); + std::sort(targetsNeedingStubs.begin(), targetsNeedingStubs.end(), + [](const Atom * left, const Atom * right) { + return (left->name().compare(right->name()) < 0); + }); + + // Make and append stubs, lazy pointers, and helpers in alphabetical order. + unsigned lazyOffset = 0; + for (const Atom *target : targetsNeedingStubs) { + StubAtom *stub = new (_file.allocator()) StubAtom(_file, _stubInfo); + LazyPointerAtom *lp = + new (_file.allocator()) LazyPointerAtom(_file, _context.is64Bit()); + StubHelperAtom *helper = + new (_file.allocator()) StubHelperAtom(_file, _stubInfo); + + addReference(stub, _stubInfo.stubReferenceToLP, lp); + addOptReference(stub, _stubInfo.stubReferenceToLP, + _stubInfo.optStubReferenceToLP, lp); + addReference(lp, _stubInfo.lazyPointerReferenceToHelper, helper); + addReference(lp, _stubInfo.lazyPointerReferenceToFinal, target); + addReference(helper, _stubInfo.stubHelperReferenceToImm, helper); + addReferenceAddend(helper, _stubInfo.stubHelperReferenceToImm, helper, + lazyOffset); + addReference(helper, _stubInfo.stubHelperReferenceToHelperCommon, + helperCommonAtom); + + mergedFile->addAtom(*stub); + mergedFile->addAtom(*lp); + mergedFile->addAtom(*helper); + + // Update each reference to use stub. + for (const Reference *ref : _targetToUses[target]) { + assert(ref->target() == target); + // Switch call site to reference stub atom instead. + const_cast<Reference *>(ref)->setTarget(stub); + } + + // Calculate new offset + lazyOffset += target->name().size() + 12; + } + } + +private: + + bool noTextRelocs() { + return true; + } + + bool isCallSite(const Reference &ref) { + return _archHandler.isCallSite(ref); + } + + void addReference(SimpleDefinedAtom* atom, + const ArchHandler::ReferenceInfo &refInfo, + const lld::Atom* target) { + atom->addReference(Reference::KindNamespace::mach_o, + refInfo.arch, refInfo.kind, refInfo.offset, + target, refInfo.addend); + } + + void addReferenceAddend(SimpleDefinedAtom *atom, + const ArchHandler::ReferenceInfo &refInfo, + const lld::Atom *target, uint64_t addend) { + atom->addReference(Reference::KindNamespace::mach_o, refInfo.arch, + refInfo.kind, refInfo.offset, target, addend); + } + + void addOptReference(SimpleDefinedAtom* atom, + const ArchHandler::ReferenceInfo &refInfo, + const ArchHandler::OptionalRefInfo &optRef, + const lld::Atom* target) { + if (!optRef.used) + return; + atom->addReference(Reference::KindNamespace::mach_o, + refInfo.arch, optRef.kind, optRef.offset, + target, optRef.addend); + } + + typedef llvm::DenseMap<const Atom*, + llvm::SmallVector<const Reference *, 8>> TargetToUses; + + const MachOLinkingContext &_context; + mach_o::ArchHandler &_archHandler; + const ArchHandler::StubInfo &_stubInfo; + MachOFile _file; + TargetToUses _targetToUses; +}; + + + +void addStubsPass(PassManager &pm, const MachOLinkingContext &ctx) { + pm.add(std::unique_ptr<Pass>(new StubsPass(ctx))); +} + +} // end namespace mach_o +} // end namespace lld diff --git a/lib/ReaderWriter/MachO/WriterMachO.cpp b/lib/ReaderWriter/MachO/WriterMachO.cpp new file mode 100644 index 0000000000000..de1c0e38063b6 --- /dev/null +++ b/lib/ReaderWriter/MachO/WriterMachO.cpp @@ -0,0 +1,72 @@ +//===- lib/ReaderWriter/MachO/WriterMachO.cpp -----------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ExecutableAtoms.hpp" +#include "MachONormalizedFile.h" +#include "lld/Core/File.h" +#include "lld/Core/Writer.h" +#include "lld/ReaderWriter/MachOLinkingContext.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/MachO.h" +#include "llvm/Support/raw_ostream.h" +#include <system_error> + +using lld::mach_o::normalized::NormalizedFile; + +namespace lld { +namespace mach_o { + +class MachOWriter : public Writer { +public: + MachOWriter(const MachOLinkingContext &ctxt) : _context(ctxt) { } + + std::error_code writeFile(const lld::File &file, StringRef path) override { + // Construct empty normalized file from atoms. + ErrorOr<std::unique_ptr<NormalizedFile>> nFile = + normalized::normalizedFromAtoms(file, _context); + if (std::error_code ec = nFile.getError()) + return ec; + + // For testing, write out yaml form of normalized file. + if (_context.printAtoms()) { + std::unique_ptr<Writer> yamlWriter = createWriterYAML(_context); + yamlWriter->writeFile(file, "-"); + } + + // Write normalized file as mach-o binary. + return writeBinary(*nFile->get(), path); + } + + bool createImplicitFiles(std::vector<std::unique_ptr<File> > &r) override { + // When building main executables, add _main as required entry point. + if (_context.outputTypeHasEntry()) + r.emplace_back(new CEntryFile(_context)); + // If this can link with dylibs, need helper function (dyld_stub_binder). + if (_context.needsStubsPass()) + r.emplace_back(new StubHelperFile(_context)); + // Final linked images can access a symbol for their mach_header. + if (_context.outputMachOType() != llvm::MachO::MH_OBJECT) + r.emplace_back(new MachHeaderAliasFile(_context)); + + return true; + } +private: + const MachOLinkingContext &_context; + }; + + +} // namespace mach_o + +std::unique_ptr<Writer> createWriterMachO(const MachOLinkingContext &context) { + return std::unique_ptr<Writer>(new lld::mach_o::MachOWriter(context)); +} + +} // namespace lld diff --git a/lib/ReaderWriter/Makefile b/lib/ReaderWriter/Makefile new file mode 100644 index 0000000000000..23587440805f3 --- /dev/null +++ b/lib/ReaderWriter/Makefile @@ -0,0 +1,16 @@ +##===- lld/lib/ReaderWriter/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 := lldReaderWriter + +# these link against this lib +PARALLEL_DIRS := ELF MachO Native PECOFF YAML + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/Native/CMakeLists.txt b/lib/ReaderWriter/Native/CMakeLists.txt new file mode 100644 index 0000000000000..e15f3d60e89c1 --- /dev/null +++ b/lib/ReaderWriter/Native/CMakeLists.txt @@ -0,0 +1,7 @@ +add_llvm_library(lldNative + ReaderNative.cpp + WriterNative.cpp + LINK_LIBS + lldCore + LLVMSupport + ) diff --git a/lib/ReaderWriter/Native/Makefile b/lib/ReaderWriter/Native/Makefile new file mode 100644 index 0000000000000..6aba37868900c --- /dev/null +++ b/lib/ReaderWriter/Native/Makefile @@ -0,0 +1,14 @@ +##===- lld/lib/ReaderWriter/Native/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 := lldNative +USEDLIBS = lldCore.a + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/Native/NativeFileFormat.h b/lib/ReaderWriter/Native/NativeFileFormat.h new file mode 100644 index 0000000000000..535072fe23148 --- /dev/null +++ b/lib/ReaderWriter/Native/NativeFileFormat.h @@ -0,0 +1,258 @@ +//===- lib/ReaderWriter/Native/NativeFileFormat.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_NATIVE_NATIVE_FILE_FORMAT_H +#define LLD_READER_WRITER_NATIVE_NATIVE_FILE_FORMAT_H + +#include "llvm/Support/DataTypes.h" +#include <cstdint> + +namespace lld { + +// +// Overview: +// +// The number one design goal of this file format is enable the linker to +// read object files into in-memory Atom objects extremely quickly. +// The second design goal is to enable future modifications to the +// Atom attribute model. +// +// The llvm native object file format is not like traditional object file +// formats (e.g. ELF, COFF, mach-o). There is no symbol table and no +// sections. Instead the file is essentially an array of archived Atoms. +// It is *not* serialized Atoms which would require deserialization into +// in memory objects. Instead it is an array of read-only info about each +// Atom. The NativeReader bulk creates in-memory Atoms which just have +// an ivar which points to the read-only info for that Atom. No additional +// processing is done to construct the in-memory Atoms. All Atom attribute +// getter methods are virtual calls which dig up the info they need from the +// ivar data. +// +// To support the gradual evolution of Atom attributes, the Atom read-only +// data is versioned. The NativeReader chooses which in-memory Atom class +// to use based on the version. What this means is that if new attributes +// are added (or changed) in the Atom model, a new native atom class and +// read-only atom info struct needs to be defined. Then, all the existing +// native reader atom classes need to be modified to do their best effort +// to map their old style read-only data to the new Atom model. At some point +// some classes to support old versions may be dropped. +// +// +// Details: +// +// The native object file format consists of a header that specifies the +// endianness of the file and the architecture along with a list of "chunks" +// in the file. A Chunk is simply a tagged range of the file. There is +// one chunk for the array of atom infos. There is another chunk for the +// string pool, and another for the content pool. +// +// It turns out there most atoms have very similar sets of attributes, only +// the name and content attribute vary. To exploit this fact to reduce the file +// size, the atom read-only info contains just the name and content info plus +// a reference to which attribute set it uses. The attribute sets are stored +// in another chunk. +// + + +// +// An entry in the NativeFileHeader that describes one chunk of the file. +// +struct NativeChunk { + uint32_t signature; + uint32_t fileOffset; + uint32_t fileSize; + uint32_t elementCount; +}; + + +// +// The header in a native object file +// +struct NativeFileHeader { + uint8_t magic[16]; + uint32_t endian; + uint32_t architecture; + uint32_t fileSize; + uint32_t chunkCount; + // NativeChunk chunks[] +}; + +// +// Possible values for NativeChunk.signature field +// +enum NativeChunkSignatures { + NCS_DefinedAtomsV1 = 1, + NCS_AttributesArrayV1 = 2, + NCS_AbsoluteAttributesV1 = 12, + NCS_UndefinedAtomsV1 = 3, + NCS_SharedLibraryAtomsV1 = 4, + NCS_AbsoluteAtomsV1 = 5, + NCS_Strings = 6, + NCS_ReferencesArrayV1 = 7, + NCS_ReferencesArrayV2 = 8, + NCS_TargetsTable = 9, + NCS_AddendsTable = 10, + NCS_Content = 11, +}; + +// +// The 16-bytes at the start of a native object file +// +#define NATIVE_FILE_HEADER_MAGIC "llvm nat obj v1 " + +// +// Possible values for the NativeFileHeader.endian field +// +enum { + NFH_BigEndian = 0x42696745, + NFH_LittleEndian = 0x4574696c +}; + + +// +// Possible values for the NativeFileHeader.architecture field +// +enum { + NFA_x86 = 1, + NFA_x86_64 = 2, + NFA_armv6 = 3, + NFA_armv7 = 4, +}; + + +// +// The NCS_DefinedAtomsV1 chunk contains an array of these structs +// +struct NativeDefinedAtomIvarsV1 { + uint32_t nameOffset; + uint32_t attributesOffset; + uint32_t referencesStartIndex; + uint32_t referencesCount; + uint32_t contentOffset; + uint32_t contentSize; + uint64_t sectionSize; +}; + + +// +// The NCS_AttributesArrayV1 chunk contains an array of these structs +// +struct NativeAtomAttributesV1 { + uint32_t sectionNameOffset; + uint16_t align2; + uint16_t alignModulus; + uint8_t scope; + uint8_t interposable; + uint8_t merge; + uint8_t contentType; + uint8_t sectionChoice; + uint8_t deadStrip; + uint8_t dynamicExport; + uint8_t permissions; + uint8_t alias; + uint8_t codeModel; +}; + + + +// +// The NCS_UndefinedAtomsV1 chunk contains an array of these structs +// +struct NativeUndefinedAtomIvarsV1 { + uint32_t nameOffset; + uint32_t flags; + uint32_t fallbackNameOffset; +}; + + +// +// The NCS_SharedLibraryAtomsV1 chunk contains an array of these structs +// +struct NativeSharedLibraryAtomIvarsV1 { + uint64_t size; + uint32_t nameOffset; + uint32_t loadNameOffset; + uint32_t type; + uint32_t flags; +}; + + + +// +// The NCS_AbsoluteAtomsV1 chunk contains an array of these structs +// +struct NativeAbsoluteAtomIvarsV1 { + uint32_t nameOffset; + uint32_t attributesOffset; + uint32_t reserved; + uint64_t value; +}; + + + +// +// The NCS_ReferencesArrayV1 chunk contains an array of these structs +// +struct NativeReferenceIvarsV1 { + enum { + noTarget = UINT16_MAX + }; + uint32_t offsetInAtom; + uint16_t kindValue; + uint8_t kindNamespace; + uint8_t kindArch; + uint16_t targetIndex; + uint16_t addendIndex; +}; + + +// +// The NCS_ReferencesArrayV2 chunk contains an array of these structs +// +struct NativeReferenceIvarsV2 { + enum : unsigned { + noTarget = UINT32_MAX + }; + uint64_t offsetInAtom; + int64_t addend; + uint16_t kindValue; + uint8_t kindNamespace; + uint8_t kindArch; + uint32_t targetIndex; + uint32_t tag; +}; + + +// +// The NCS_TargetsTable chunk contains an array of uint32_t entries. +// The C++ class Reference has a target() method that returns a +// pointer to another Atom. We can't have pointers in object files, +// so instead NativeReferenceIvarsV1 contains an index to the target. +// The index is into this NCS_TargetsTable of uint32_t entries. +// The values in this table are the index of the (target) atom in this file. +// For DefinedAtoms the value is from 0 to NCS_DefinedAtomsV1.elementCount. +// For UndefinedAtoms the value is from NCS_DefinedAtomsV1.elementCount to +// NCS_DefinedAtomsV1.elementCount+NCS_UndefinedAtomsV1.elementCount. +// + + +// +// The NCS_AddendsTable chunk contains an array of int64_t entries. +// If we allocated space for addends directly in NativeReferenceIvarsV1 +// it would double the size of that struct. But since addends are rare, +// we instead just keep a pool of addends and have NativeReferenceIvarsV1 +// (if it needs an addend) just store the index (into the pool) of the +// addend it needs. +// + + + +} // namespace lld + +#endif // LLD_READER_WRITER_NATIVE_NATIVE_FILE_FORMAT_H diff --git a/lib/ReaderWriter/Native/ReaderNative.cpp b/lib/ReaderWriter/Native/ReaderNative.cpp new file mode 100644 index 0000000000000..84cdb4b997e8f --- /dev/null +++ b/lib/ReaderWriter/Native/ReaderNative.cpp @@ -0,0 +1,1013 @@ +//===- lib/ReaderWriter/Native/ReaderNative.cpp ---------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "NativeFileFormat.h" +#include "lld/Core/Atom.h" +#include "lld/Core/Error.h" +#include "lld/Core/File.h" +#include "lld/Core/Reader.h" +#include "lld/Core/Simple.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include <memory> +#include <vector> + +namespace lld { +namespace native { + +// forward reference +class File; + +// +// An object of this class is instantied for each NativeDefinedAtomIvarsV1 +// struct in the NCS_DefinedAtomsV1 chunk. +// +class NativeDefinedAtomV1 : public DefinedAtom { +public: + NativeDefinedAtomV1(const File& f, + const NativeDefinedAtomIvarsV1* ivarData) + : _file(&f), _ivarData(ivarData) { } + + const lld::File& file() const override; + + uint64_t ordinal() const override; + + StringRef name() const override; + + uint64_t size() const override { return _ivarData->contentSize; } + + uint64_t sectionSize() const override { return _ivarData->sectionSize; } + + DefinedAtom::Scope scope() const override { + return (DefinedAtom::Scope)(attributes().scope); + } + + DefinedAtom::Interposable interposable() const override { + return (DefinedAtom::Interposable)(attributes().interposable); + } + + DefinedAtom::Merge merge() const override { + return (DefinedAtom::Merge)(attributes().merge); + } + + DefinedAtom::ContentType contentType() const override { + const NativeAtomAttributesV1& attr = attributes(); + return (DefinedAtom::ContentType)(attr.contentType); + } + + DefinedAtom::Alignment alignment() const override { + return DefinedAtom::Alignment(attributes().align2, attributes().alignModulus); + } + + DefinedAtom::SectionChoice sectionChoice() const override { + return (DefinedAtom::SectionChoice)(attributes().sectionChoice); + } + + StringRef customSectionName() const override; + + DefinedAtom::DeadStripKind deadStrip() const override { + return (DefinedAtom::DeadStripKind)(attributes().deadStrip); + } + + DynamicExport dynamicExport() const override { + return (DynamicExport)attributes().dynamicExport; + } + + DefinedAtom::CodeModel codeModel() const override { + return DefinedAtom::CodeModel(attributes().codeModel); + } + + DefinedAtom::ContentPermissions permissions() const override { + return (DefinedAtom::ContentPermissions)(attributes().permissions); + } + + ArrayRef<uint8_t> rawContent() const override; + + reference_iterator begin() const override; + + reference_iterator end() const override; + + const Reference* derefIterator(const void*) const override; + + void incrementIterator(const void*& it) const override; + +private: + const NativeAtomAttributesV1& attributes() const; + + const File *_file; + const NativeDefinedAtomIvarsV1 *_ivarData; +}; + + + +// +// An object of this class is instantied for each NativeUndefinedAtomIvarsV1 +// struct in the NCS_UndefinedAtomsV1 chunk. +// +class NativeUndefinedAtomV1 : public UndefinedAtom { +public: + NativeUndefinedAtomV1(const File& f, + const NativeUndefinedAtomIvarsV1* ivarData) + : _file(&f), _ivarData(ivarData) { } + + const lld::File& file() const override; + StringRef name() const override; + + CanBeNull canBeNull() const override { + return (CanBeNull)(_ivarData->flags & 0x3); + } + + const UndefinedAtom *fallback() const override; + +private: + const File *_file; + const NativeUndefinedAtomIvarsV1 *_ivarData; + mutable std::unique_ptr<const SimpleUndefinedAtom> _fallback; +}; + + +// +// An object of this class is instantied for each NativeUndefinedAtomIvarsV1 +// struct in the NCS_SharedLibraryAtomsV1 chunk. +// +class NativeSharedLibraryAtomV1 : public SharedLibraryAtom { +public: + NativeSharedLibraryAtomV1(const File& f, + const NativeSharedLibraryAtomIvarsV1* ivarData) + : _file(&f), _ivarData(ivarData) { } + + const lld::File& file() const override; + StringRef name() const override; + StringRef loadName() const override; + + bool canBeNullAtRuntime() const override { + return (_ivarData->flags & 0x1); + } + + Type type() const override { + return (Type)_ivarData->type; + } + + uint64_t size() const override { + return _ivarData->size; + } + +private: + const File *_file; + const NativeSharedLibraryAtomIvarsV1 *_ivarData; +}; + + +// +// An object of this class is instantied for each NativeAbsoluteAtomIvarsV1 +// struct in the NCS_AbsoluteAtomsV1 chunk. +// +class NativeAbsoluteAtomV1 : public AbsoluteAtom { +public: + NativeAbsoluteAtomV1(const File& f, + const NativeAbsoluteAtomIvarsV1* ivarData) + : _file(&f), _ivarData(ivarData) { } + + const lld::File& file() const override; + StringRef name() const override; + Scope scope() const override { + const NativeAtomAttributesV1& attr = absAttributes(); + return (Scope)(attr.scope); + } + uint64_t value() const override { + return _ivarData->value; + } + +private: + const NativeAtomAttributesV1& absAttributes() const; + const File *_file; + const NativeAbsoluteAtomIvarsV1 *_ivarData; +}; + + +// +// An object of this class is instantied for each NativeReferenceIvarsV1 +// struct in the NCS_ReferencesArrayV1 chunk. +// +class NativeReferenceV1 : public Reference { +public: + NativeReferenceV1(const File &f, const NativeReferenceIvarsV1 *ivarData) + : Reference((KindNamespace)ivarData->kindNamespace, + (KindArch)ivarData->kindArch, ivarData->kindValue), + _file(&f), _ivarData(ivarData) {} + + uint64_t offsetInAtom() const override { + return _ivarData->offsetInAtom; + } + + const Atom* target() const override; + Addend addend() const override; + void setTarget(const Atom* newAtom) override; + void setAddend(Addend a) override; + +private: + const File *_file; + const NativeReferenceIvarsV1 *_ivarData; +}; + + +// +// An object of this class is instantied for each NativeReferenceIvarsV1 +// struct in the NCS_ReferencesArrayV1 chunk. +// +class NativeReferenceV2 : public Reference { +public: + NativeReferenceV2(const File &f, const NativeReferenceIvarsV2 *ivarData) + : Reference((KindNamespace)ivarData->kindNamespace, + (KindArch)ivarData->kindArch, ivarData->kindValue), + _file(&f), _ivarData(ivarData) {} + + uint64_t offsetInAtom() const override { + return _ivarData->offsetInAtom; + } + + const Atom* target() const override; + Addend addend() const override; + void setTarget(const Atom* newAtom) override; + void setAddend(Addend a) override; + uint32_t tag() const override; + +private: + const File *_file; + const NativeReferenceIvarsV2 *_ivarData; +}; + + +// +// lld::File object for native llvm object file +// +class File : public lld::File { +public: + File(std::unique_ptr<MemoryBuffer> mb) + : lld::File(mb->getBufferIdentifier(), kindObject), + _mb(std::move(mb)), // Reader now takes ownership of buffer + _header(nullptr), _targetsTable(nullptr), _targetsTableCount(0), + _strings(nullptr), _stringsMaxOffset(0), _addends(nullptr), + _addendsMaxIndex(0), _contentStart(nullptr), _contentEnd(nullptr) { + _header = + reinterpret_cast<const NativeFileHeader *>(_mb->getBufferStart()); + } + + /// Parses a File object from a native object file. + std::error_code doParse() override { + const uint8_t *const base = + reinterpret_cast<const uint8_t *>(_mb->getBufferStart()); + StringRef path(_mb->getBufferIdentifier()); + const NativeFileHeader *const header = + reinterpret_cast<const NativeFileHeader *>(base); + const NativeChunk *const chunks = + reinterpret_cast<const NativeChunk *>(base + sizeof(NativeFileHeader)); + // make sure magic matches + if (memcmp(header->magic, NATIVE_FILE_HEADER_MAGIC, + sizeof(header->magic)) != 0) + return make_error_code(NativeReaderError::unknown_file_format); + + // make sure mapped file contains all needed data + const size_t fileSize = _mb->getBufferSize(); + if (header->fileSize > fileSize) + return make_error_code(NativeReaderError::file_too_short); + + DEBUG_WITH_TYPE("ReaderNative", + llvm::dbgs() << " Native File Header:" << " fileSize=" + << header->fileSize << " chunkCount=" + << header->chunkCount << "\n"); + + // process each chunk + for (uint32_t i = 0; i < header->chunkCount; ++i) { + std::error_code ec; + const NativeChunk* chunk = &chunks[i]; + // sanity check chunk is within file + if ( chunk->fileOffset > fileSize ) + return make_error_code(NativeReaderError::file_malformed); + if ( (chunk->fileOffset + chunk->fileSize) > fileSize) + return make_error_code(NativeReaderError::file_malformed); + // process chunk, based on signature + switch ( chunk->signature ) { + case NCS_DefinedAtomsV1: + ec = processDefinedAtomsV1(base, chunk); + break; + case NCS_AttributesArrayV1: + ec = processAttributesV1(base, chunk); + break; + case NCS_UndefinedAtomsV1: + ec = processUndefinedAtomsV1(base, chunk); + break; + case NCS_SharedLibraryAtomsV1: + ec = processSharedLibraryAtomsV1(base, chunk); + break; + case NCS_AbsoluteAtomsV1: + ec = processAbsoluteAtomsV1(base, chunk); + break; + case NCS_AbsoluteAttributesV1: + ec = processAbsoluteAttributesV1(base, chunk); + break; + case NCS_ReferencesArrayV1: + ec = processReferencesV1(base, chunk); + break; + case NCS_ReferencesArrayV2: + ec = processReferencesV2(base, chunk); + break; + case NCS_TargetsTable: + ec = processTargetsTable(base, chunk); + break; + case NCS_AddendsTable: + ec = processAddendsTable(base, chunk); + break; + case NCS_Content: + ec = processContent(base, chunk); + break; + case NCS_Strings: + ec = processStrings(base, chunk); + break; + default: + return make_error_code(NativeReaderError::unknown_chunk_type); + } + if ( ec ) { + return ec; + } + } + // TO DO: validate enough chunks were used + + DEBUG_WITH_TYPE("ReaderNative", { + llvm::dbgs() << " ReaderNative DefinedAtoms:\n"; + for (const DefinedAtom *a : defined()) { + llvm::dbgs() << llvm::format(" 0x%09lX", a) + << ", name=" << a->name() + << ", size=" << a->size() << "\n"; + for (const Reference *r : *a) { + llvm::dbgs() << " offset=" + << llvm::format("0x%03X", r->offsetInAtom()) + << ", kind=" << r->kindValue() + << ", target=" << r->target() << "\n"; + } + } + }); + return make_error_code(NativeReaderError::success); + } + + virtual ~File() { + // _mb is automatically deleted because of std::unique_ptr<> + + // All other ivar pointers are pointers into the MemoryBuffer, except + // the _definedAtoms array which was allocated to contain an array + // of Atom objects. The atoms have empty destructors, so it is ok + // to just delete the memory. + delete _definedAtoms._arrayStart; + delete _undefinedAtoms._arrayStart; + delete _sharedLibraryAtoms._arrayStart; + delete _absoluteAtoms._arrayStart; + delete _referencesV1.arrayStart; + delete _referencesV2.arrayStart; + delete [] _targetsTable; + } + + 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; + } + +private: + friend NativeDefinedAtomV1; + friend NativeUndefinedAtomV1; + friend NativeSharedLibraryAtomV1; + friend NativeAbsoluteAtomV1; + friend NativeReferenceV1; + friend NativeReferenceV2; + + // instantiate array of DefinedAtoms from v1 ivar data in file + std::error_code processDefinedAtomsV1(const uint8_t *base, + const NativeChunk *chunk) { + const size_t atomSize = sizeof(NativeDefinedAtomV1); + size_t atomsArraySize = chunk->elementCount * atomSize; + uint8_t* atomsStart = reinterpret_cast<uint8_t*> + (operator new(atomsArraySize, std::nothrow)); + if (atomsStart == nullptr) + return make_error_code(NativeReaderError::memory_error); + const size_t ivarElementSize = chunk->fileSize + / chunk->elementCount; + if ( ivarElementSize != sizeof(NativeDefinedAtomIvarsV1) ) + return make_error_code(NativeReaderError::file_malformed); + uint8_t* atomsEnd = atomsStart + atomsArraySize; + const NativeDefinedAtomIvarsV1* ivarData = + reinterpret_cast<const NativeDefinedAtomIvarsV1*> + (base + chunk->fileOffset); + for(uint8_t* s = atomsStart; s != atomsEnd; s += atomSize) { + NativeDefinedAtomV1* atomAllocSpace = + reinterpret_cast<NativeDefinedAtomV1*>(s); + new (atomAllocSpace) NativeDefinedAtomV1(*this, ivarData); + ++ivarData; + } + this->_definedAtoms._arrayStart = atomsStart; + this->_definedAtoms._arrayEnd = atomsEnd; + this->_definedAtoms._elementSize = atomSize; + this->_definedAtoms._elementCount = chunk->elementCount; + DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() + << " chunk DefinedAtomsV1: " + << " count=" << chunk->elementCount + << " chunkSize=" << chunk->fileSize + << "\n"); + return make_error_code(NativeReaderError::success); + } + + + + // set up pointers to attributes array + std::error_code processAttributesV1(const uint8_t *base, + const NativeChunk *chunk) { + this->_attributes = base + chunk->fileOffset; + this->_attributesMaxOffset = chunk->fileSize; + DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() + << " chunk AttributesV1: " + << " count=" << chunk->elementCount + << " chunkSize=" << chunk->fileSize + << "\n"); + return make_error_code(NativeReaderError::success); + } + + // set up pointers to attributes array + std::error_code processAbsoluteAttributesV1(const uint8_t *base, + const NativeChunk *chunk) { + this->_absAttributes = base + chunk->fileOffset; + this->_absAbsoluteMaxOffset = chunk->fileSize; + DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() + << " chunk AbsoluteAttributesV1: " + << " count=" << chunk->elementCount + << " chunkSize=" << chunk->fileSize + << "\n"); + return make_error_code(NativeReaderError::success); + } + + // instantiate array of UndefinedAtoms from v1 ivar data in file + std::error_code processUndefinedAtomsV1(const uint8_t *base, + const NativeChunk *chunk) { + const size_t atomSize = sizeof(NativeUndefinedAtomV1); + size_t atomsArraySize = chunk->elementCount * atomSize; + uint8_t* atomsStart = reinterpret_cast<uint8_t*> + (operator new(atomsArraySize, std::nothrow)); + if (atomsStart == nullptr) + return make_error_code(NativeReaderError::memory_error); + const size_t ivarElementSize = chunk->fileSize + / chunk->elementCount; + if ( ivarElementSize != sizeof(NativeUndefinedAtomIvarsV1) ) + return make_error_code(NativeReaderError::file_malformed); + uint8_t* atomsEnd = atomsStart + atomsArraySize; + const NativeUndefinedAtomIvarsV1* ivarData = + reinterpret_cast<const NativeUndefinedAtomIvarsV1*> + (base + chunk->fileOffset); + for(uint8_t* s = atomsStart; s != atomsEnd; s += atomSize) { + NativeUndefinedAtomV1* atomAllocSpace = + reinterpret_cast<NativeUndefinedAtomV1*>(s); + new (atomAllocSpace) NativeUndefinedAtomV1(*this, ivarData); + ++ivarData; + } + this->_undefinedAtoms._arrayStart = atomsStart; + this->_undefinedAtoms._arrayEnd = atomsEnd; + this->_undefinedAtoms._elementSize = atomSize; + this->_undefinedAtoms._elementCount = chunk->elementCount; + DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() + << " chunk UndefinedAtomsV1:" + << " count=" << chunk->elementCount + << " chunkSize=" << chunk->fileSize + << "\n"); + return make_error_code(NativeReaderError::success); + } + + + // instantiate array of ShareLibraryAtoms from v1 ivar data in file + std::error_code processSharedLibraryAtomsV1(const uint8_t *base, + const NativeChunk *chunk) { + const size_t atomSize = sizeof(NativeSharedLibraryAtomV1); + size_t atomsArraySize = chunk->elementCount * atomSize; + uint8_t* atomsStart = reinterpret_cast<uint8_t*> + (operator new(atomsArraySize, std::nothrow)); + if (atomsStart == nullptr) + return make_error_code(NativeReaderError::memory_error); + const size_t ivarElementSize = chunk->fileSize + / chunk->elementCount; + if ( ivarElementSize != sizeof(NativeSharedLibraryAtomIvarsV1) ) + return make_error_code(NativeReaderError::file_malformed); + uint8_t* atomsEnd = atomsStart + atomsArraySize; + const NativeSharedLibraryAtomIvarsV1* ivarData = + reinterpret_cast<const NativeSharedLibraryAtomIvarsV1*> + (base + chunk->fileOffset); + for(uint8_t* s = atomsStart; s != atomsEnd; s += atomSize) { + NativeSharedLibraryAtomV1* atomAllocSpace = + reinterpret_cast<NativeSharedLibraryAtomV1*>(s); + new (atomAllocSpace) NativeSharedLibraryAtomV1(*this, ivarData); + ++ivarData; + } + this->_sharedLibraryAtoms._arrayStart = atomsStart; + this->_sharedLibraryAtoms._arrayEnd = atomsEnd; + this->_sharedLibraryAtoms._elementSize = atomSize; + this->_sharedLibraryAtoms._elementCount = chunk->elementCount; + DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() + << " chunk SharedLibraryAtomsV1:" + << " count=" << chunk->elementCount + << " chunkSize=" << chunk->fileSize + << "\n"); + return make_error_code(NativeReaderError::success); + } + + + // instantiate array of AbsoluteAtoms from v1 ivar data in file + std::error_code processAbsoluteAtomsV1(const uint8_t *base, + const NativeChunk *chunk) { + const size_t atomSize = sizeof(NativeAbsoluteAtomV1); + size_t atomsArraySize = chunk->elementCount * atomSize; + uint8_t* atomsStart = reinterpret_cast<uint8_t*> + (operator new(atomsArraySize, std::nothrow)); + if (atomsStart == nullptr) + return make_error_code(NativeReaderError::memory_error); + const size_t ivarElementSize = chunk->fileSize + / chunk->elementCount; + if ( ivarElementSize != sizeof(NativeAbsoluteAtomIvarsV1) ) + return make_error_code(NativeReaderError::file_malformed); + uint8_t* atomsEnd = atomsStart + atomsArraySize; + const NativeAbsoluteAtomIvarsV1* ivarData = + reinterpret_cast<const NativeAbsoluteAtomIvarsV1*> + (base + chunk->fileOffset); + for(uint8_t* s = atomsStart; s != atomsEnd; s += atomSize) { + NativeAbsoluteAtomV1* atomAllocSpace = + reinterpret_cast<NativeAbsoluteAtomV1*>(s); + new (atomAllocSpace) NativeAbsoluteAtomV1(*this, ivarData); + ++ivarData; + } + this->_absoluteAtoms._arrayStart = atomsStart; + this->_absoluteAtoms._arrayEnd = atomsEnd; + this->_absoluteAtoms._elementSize = atomSize; + this->_absoluteAtoms._elementCount = chunk->elementCount; + DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() + << " chunk AbsoluteAtomsV1: " + << " count=" << chunk->elementCount + << " chunkSize=" << chunk->fileSize + << "\n"); + return make_error_code(NativeReaderError::success); + } + + template <class T, class U> + std::error_code + processReferences(const uint8_t *base, const NativeChunk *chunk, + uint8_t *&refsStart, uint8_t *&refsEnd) const { + if (chunk->elementCount == 0) + return make_error_code(NativeReaderError::success); + size_t refsArraySize = chunk->elementCount * sizeof(T); + refsStart = reinterpret_cast<uint8_t *>( + operator new(refsArraySize, std::nothrow)); + if (refsStart == nullptr) + return make_error_code(NativeReaderError::memory_error); + const size_t ivarElementSize = chunk->fileSize / chunk->elementCount; + if (ivarElementSize != sizeof(U)) + return make_error_code(NativeReaderError::file_malformed); + refsEnd = refsStart + refsArraySize; + const U* ivarData = reinterpret_cast<const U *>(base + chunk->fileOffset); + for (uint8_t *s = refsStart; s != refsEnd; s += sizeof(T), ++ivarData) { + T *atomAllocSpace = reinterpret_cast<T *>(s); + new (atomAllocSpace) T(*this, ivarData); + } + return make_error_code(NativeReaderError::success); + } + + // instantiate array of References from v1 ivar data in file + std::error_code processReferencesV1(const uint8_t *base, + const NativeChunk *chunk) { + uint8_t *refsStart, *refsEnd; + if (std::error_code ec = + processReferences<NativeReferenceV1, NativeReferenceIvarsV1>( + base, chunk, refsStart, refsEnd)) + return ec; + this->_referencesV1.arrayStart = refsStart; + this->_referencesV1.arrayEnd = refsEnd; + this->_referencesV1.elementSize = sizeof(NativeReferenceV1); + this->_referencesV1.elementCount = chunk->elementCount; + DEBUG_WITH_TYPE("ReaderNative", { + llvm::dbgs() << " chunk ReferencesV1: " + << " count=" << chunk->elementCount + << " chunkSize=" << chunk->fileSize << "\n"; + }); + return make_error_code(NativeReaderError::success); + } + + // instantiate array of References from v2 ivar data in file + std::error_code processReferencesV2(const uint8_t *base, + const NativeChunk *chunk) { + uint8_t *refsStart, *refsEnd; + if (std::error_code ec = + processReferences<NativeReferenceV2, NativeReferenceIvarsV2>( + base, chunk, refsStart, refsEnd)) + return ec; + this->_referencesV2.arrayStart = refsStart; + this->_referencesV2.arrayEnd = refsEnd; + this->_referencesV2.elementSize = sizeof(NativeReferenceV2); + this->_referencesV2.elementCount = chunk->elementCount; + DEBUG_WITH_TYPE("ReaderNative", { + llvm::dbgs() << " chunk ReferencesV2: " + << " count=" << chunk->elementCount + << " chunkSize=" << chunk->fileSize << "\n"; + }); + return make_error_code(NativeReaderError::success); + } + + // set up pointers to target table + std::error_code processTargetsTable(const uint8_t *base, + const NativeChunk *chunk) { + const uint32_t* targetIndexes = reinterpret_cast<const uint32_t*> + (base + chunk->fileOffset); + this->_targetsTableCount = chunk->elementCount; + this->_targetsTable = new const Atom*[chunk->elementCount]; + for (uint32_t i=0; i < chunk->elementCount; ++i) { + const uint32_t index = targetIndexes[i]; + if ( index < _definedAtoms._elementCount ) { + const uint8_t* p = _definedAtoms._arrayStart + + index * _definedAtoms._elementSize; + this->_targetsTable[i] = reinterpret_cast<const DefinedAtom*>(p); + continue; + } + const uint32_t undefIndex = index - _definedAtoms._elementCount; + if ( undefIndex < _undefinedAtoms._elementCount ) { + const uint8_t* p = _undefinedAtoms._arrayStart + + undefIndex * _undefinedAtoms._elementSize; + this->_targetsTable[i] = reinterpret_cast<const UndefinedAtom*>(p); + continue; + } + const uint32_t slIndex = index - _definedAtoms._elementCount + - _undefinedAtoms._elementCount; + if ( slIndex < _sharedLibraryAtoms._elementCount ) { + const uint8_t* p = _sharedLibraryAtoms._arrayStart + + slIndex * _sharedLibraryAtoms._elementSize; + this->_targetsTable[i] = reinterpret_cast<const SharedLibraryAtom*>(p); + continue; + } + const uint32_t abIndex = index - _definedAtoms._elementCount + - _undefinedAtoms._elementCount + - _sharedLibraryAtoms._elementCount; + if ( abIndex < _absoluteAtoms._elementCount ) { + const uint8_t* p = _absoluteAtoms._arrayStart + + abIndex * _absoluteAtoms._elementSize; + this->_targetsTable[i] = reinterpret_cast<const AbsoluteAtom*>(p); + continue; + } + return make_error_code(NativeReaderError::file_malformed); + } + DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() + << " chunk Targets Table: " + << " count=" << chunk->elementCount + << " chunkSize=" << chunk->fileSize + << "\n"); + return make_error_code(NativeReaderError::success); + } + + + // set up pointers to addend pool in file + std::error_code processAddendsTable(const uint8_t *base, + const NativeChunk *chunk) { + this->_addends = reinterpret_cast<const Reference::Addend*> + (base + chunk->fileOffset); + this->_addendsMaxIndex = chunk->elementCount; + DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() + << " chunk Addends: " + << " count=" << chunk->elementCount + << " chunkSize=" << chunk->fileSize + << "\n"); + return make_error_code(NativeReaderError::success); + } + + // set up pointers to string pool in file + std::error_code processStrings(const uint8_t *base, + const NativeChunk *chunk) { + this->_strings = reinterpret_cast<const char*>(base + chunk->fileOffset); + this->_stringsMaxOffset = chunk->fileSize; + DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() + << " chunk Strings: " + << " chunkSize=" << chunk->fileSize + << "\n"); + return make_error_code(NativeReaderError::success); + } + + // set up pointers to content area in file + std::error_code processContent(const uint8_t *base, + const NativeChunk *chunk) { + this->_contentStart = base + chunk->fileOffset; + this->_contentEnd = base + chunk->fileOffset + chunk->fileSize; + DEBUG_WITH_TYPE("ReaderNative", llvm::dbgs() + << " chunk content: " + << " chunkSize=" << chunk->fileSize + << "\n"); + return make_error_code(NativeReaderError::success); + } + + StringRef string(uint32_t offset) const { + assert(offset < _stringsMaxOffset); + return StringRef(&_strings[offset]); + } + + Reference::Addend addend(uint32_t index) const { + if ( index == 0 ) + return 0; // addend index zero is used to mean "no addend" + assert(index <= _addendsMaxIndex); + return _addends[index-1]; // one-based indexing + } + + const NativeAtomAttributesV1& attribute(uint32_t off) const { + assert(off < _attributesMaxOffset); + return *reinterpret_cast<const NativeAtomAttributesV1*>(_attributes + off); + } + + const NativeAtomAttributesV1& absAttribute(uint32_t off) const { + assert(off < _absAbsoluteMaxOffset); + return *reinterpret_cast<const NativeAtomAttributesV1*>(_absAttributes + off); + } + + const uint8_t* content(uint32_t offset, uint32_t size) const { + const uint8_t* result = _contentStart + offset; + assert((result+size) <= _contentEnd); + return result; + } + + const Reference* referenceByIndex(uintptr_t index) const { + if (index < _referencesV1.elementCount) { + return reinterpret_cast<const NativeReferenceV1*>( + _referencesV1.arrayStart + index * _referencesV1.elementSize); + } + assert(index < _referencesV2.elementCount); + return reinterpret_cast<const NativeReferenceV2*>( + _referencesV2.arrayStart + index * _referencesV2.elementSize); + } + + const Atom* targetV1(uint16_t index) const { + if ( index == NativeReferenceIvarsV1::noTarget ) + return nullptr; + assert(index < _targetsTableCount); + return _targetsTable[index]; + } + + void setTargetV1(uint16_t index, const Atom* newAtom) const { + assert(index != NativeReferenceIvarsV1::noTarget); + assert(index > _targetsTableCount); + _targetsTable[index] = newAtom; + } + + const Atom* targetV2(uint32_t index) const { + if (index == NativeReferenceIvarsV2::noTarget) + return nullptr; + assert(index < _targetsTableCount); + return _targetsTable[index]; + } + + void setTargetV2(uint32_t index, const Atom* newAtom) const { + assert(index != NativeReferenceIvarsV2::noTarget); + assert(index > _targetsTableCount); + _targetsTable[index] = newAtom; + } + + template <typename T> + class AtomArray : public File::atom_collection<T> { + public: + AtomArray() : _arrayStart(nullptr), _arrayEnd(nullptr), + _elementSize(0), _elementCount(0) { } + + virtual atom_iterator<T> begin() const { + return atom_iterator<T>(*this, reinterpret_cast<const void*>(_arrayStart)); + } + virtual atom_iterator<T> end() const{ + return atom_iterator<T>(*this, reinterpret_cast<const void*>(_arrayEnd)); + } + virtual const T* deref(const void* it) const { + return reinterpret_cast<const T*>(it); + } + virtual void next(const void*& it) const { + const uint8_t* p = reinterpret_cast<const uint8_t*>(it); + p += _elementSize; + it = reinterpret_cast<const void*>(p); + } + virtual uint64_t size() const { return _elementCount; } + const uint8_t *_arrayStart; + const uint8_t *_arrayEnd; + uint32_t _elementSize; + uint32_t _elementCount; + }; + + struct IvarArray { + IvarArray() : + arrayStart(nullptr), + arrayEnd(nullptr), + elementSize(0), + elementCount(0) { } + + const uint8_t* arrayStart; + const uint8_t* arrayEnd; + uint32_t elementSize; + uint32_t elementCount; + }; + + std::unique_ptr<MemoryBuffer> _mb; + const NativeFileHeader* _header; + AtomArray<DefinedAtom> _definedAtoms; + AtomArray<UndefinedAtom> _undefinedAtoms; + AtomArray<SharedLibraryAtom> _sharedLibraryAtoms; + AtomArray<AbsoluteAtom> _absoluteAtoms; + const uint8_t* _absAttributes; + uint32_t _absAbsoluteMaxOffset; + const uint8_t* _attributes; + uint32_t _attributesMaxOffset; + IvarArray _referencesV1; + IvarArray _referencesV2; + const Atom** _targetsTable; + uint32_t _targetsTableCount; + const char* _strings; + uint32_t _stringsMaxOffset; + const Reference::Addend* _addends; + uint32_t _addendsMaxIndex; + const uint8_t *_contentStart; + const uint8_t *_contentEnd; +}; + +inline const lld::File &NativeDefinedAtomV1::file() const { + return *_file; +} + +inline uint64_t NativeDefinedAtomV1:: ordinal() const { + const uint8_t* p = reinterpret_cast<const uint8_t*>(_ivarData); + return p - _file->_definedAtoms._arrayStart; +} + +inline StringRef NativeDefinedAtomV1::name() const { + return _file->string(_ivarData->nameOffset); +} + +inline const NativeAtomAttributesV1& NativeDefinedAtomV1::attributes() const { + return _file->attribute(_ivarData->attributesOffset); +} + +inline ArrayRef<uint8_t> NativeDefinedAtomV1::rawContent() const { + if (!occupiesDiskSpace()) + return ArrayRef<uint8_t>(); + const uint8_t* p = _file->content(_ivarData->contentOffset, + _ivarData->contentSize); + return ArrayRef<uint8_t>(p, _ivarData->contentSize); +} + +inline StringRef NativeDefinedAtomV1::customSectionName() const { + uint32_t offset = attributes().sectionNameOffset; + return _file->string(offset); +} + +DefinedAtom::reference_iterator NativeDefinedAtomV1::begin() const { + uintptr_t index = _ivarData->referencesStartIndex; + const void* it = reinterpret_cast<const void*>(index); + return reference_iterator(*this, it); +} + +DefinedAtom::reference_iterator NativeDefinedAtomV1::end() const { + uintptr_t index = _ivarData->referencesStartIndex+_ivarData->referencesCount; + const void* it = reinterpret_cast<const void*>(index); + return reference_iterator(*this, it); +} + +const Reference* NativeDefinedAtomV1::derefIterator(const void* it) const { + uintptr_t index = reinterpret_cast<uintptr_t>(it); + return _file->referenceByIndex(index); +} + +void NativeDefinedAtomV1::incrementIterator(const void*& it) const { + uintptr_t index = reinterpret_cast<uintptr_t>(it); + ++index; + it = reinterpret_cast<const void*>(index); +} + +inline const lld::File& NativeUndefinedAtomV1::file() const { + return *_file; +} + +inline StringRef NativeUndefinedAtomV1::name() const { + return _file->string(_ivarData->nameOffset); +} + +inline const UndefinedAtom *NativeUndefinedAtomV1::fallback() const { + if (!_ivarData->fallbackNameOffset) + return nullptr; + if (!_fallback) + _fallback.reset(new SimpleUndefinedAtom( + *_file, _file->string(_ivarData->fallbackNameOffset))); + return _fallback.get(); +} + +inline const lld::File& NativeSharedLibraryAtomV1::file() const { + return *_file; +} + +inline StringRef NativeSharedLibraryAtomV1::name() const { + return _file->string(_ivarData->nameOffset); +} + +inline StringRef NativeSharedLibraryAtomV1::loadName() const { + return _file->string(_ivarData->loadNameOffset); +} + + + +inline const lld::File& NativeAbsoluteAtomV1::file() const { + return *_file; +} + +inline StringRef NativeAbsoluteAtomV1::name() const { + return _file->string(_ivarData->nameOffset); +} + +inline const NativeAtomAttributesV1& NativeAbsoluteAtomV1::absAttributes() const { + return _file->absAttribute(_ivarData->attributesOffset); +} + +inline const Atom* NativeReferenceV1::target() const { + return _file->targetV1(_ivarData->targetIndex); +} + +inline Reference::Addend NativeReferenceV1::addend() const { + return _file->addend(_ivarData->addendIndex); +} + +inline void NativeReferenceV1::setTarget(const Atom* newAtom) { + return _file->setTargetV1(_ivarData->targetIndex, newAtom); +} + +inline void NativeReferenceV1::setAddend(Addend a) { + // Do nothing if addend value is not being changed. + if (addend() == a) + return; + llvm_unreachable("setAddend() not supported"); +} + +inline const Atom* NativeReferenceV2::target() const { + return _file->targetV2(_ivarData->targetIndex); +} + +inline Reference::Addend NativeReferenceV2::addend() const { + return _ivarData->addend; +} + +inline void NativeReferenceV2::setTarget(const Atom* newAtom) { + return _file->setTargetV2(_ivarData->targetIndex, newAtom); +} + +inline void NativeReferenceV2::setAddend(Addend a) { + // Do nothing if addend value is not being changed. + if (addend() == a) + return; + llvm_unreachable("setAddend() not supported"); +} + +uint32_t NativeReferenceV2::tag() const { return _ivarData->tag; } + +} // end namespace native + +namespace { + +class NativeReader : public Reader { +public: + virtual bool canParse(file_magic magic, StringRef, + const MemoryBuffer &mb) const override { + const NativeFileHeader *const header = + reinterpret_cast<const NativeFileHeader *>(mb.getBufferStart()); + return (memcmp(header->magic, NATIVE_FILE_HEADER_MAGIC, + sizeof(header->magic)) == 0); + } + + virtual std::error_code + loadFile(std::unique_ptr<MemoryBuffer> mb, const class Registry &, + std::vector<std::unique_ptr<File>> &result) const override { + auto *file = new lld::native::File(std::move(mb)); + result.push_back(std::unique_ptr<File>(file)); + return std::error_code(); + } +}; + +} + +void Registry::addSupportNativeObjects() { + add(std::unique_ptr<Reader>(new NativeReader())); +} + +} // end namespace lld diff --git a/lib/ReaderWriter/Native/WriterNative.cpp b/lib/ReaderWriter/Native/WriterNative.cpp new file mode 100644 index 0000000000000..5e01a6ce1c7c7 --- /dev/null +++ b/lib/ReaderWriter/Native/WriterNative.cpp @@ -0,0 +1,566 @@ +//===- lib/ReaderWriter/Native/WriterNative.cpp ---------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "NativeFileFormat.h" +#include "lld/Core/File.h" +#include "lld/Core/LinkingContext.h" +#include "lld/Core/Writer.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" +#include <cstdint> +#include <set> +#include <system_error> +#include <vector> + +namespace lld { +namespace native { + +/// +/// Class for writing native object files. +/// +class Writer : public lld::Writer { +public: + std::error_code writeFile(const lld::File &file, StringRef outPath) override { + // reserve first byte for unnamed atoms + _stringPool.push_back('\0'); + // visit all atoms + for ( const DefinedAtom *defAtom : file.defined() ) { + this->addIVarsForDefinedAtom(*defAtom); + // We are trying to process all atoms, but the defined() iterator does not + // return group children. So, when a group parent is found, we need to + // handle each child atom. + if (defAtom->isGroupParent()) { + for (const Reference *r : *defAtom) { + if (r->kindNamespace() != lld::Reference::KindNamespace::all) + continue; + if (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"); + this->addIVarsForDefinedAtom(*target); + } + } + } + } + for ( const UndefinedAtom *undefAtom : file.undefined() ) { + this->addIVarsForUndefinedAtom(*undefAtom); + } + for ( const SharedLibraryAtom *shlibAtom : file.sharedLibrary() ) { + this->addIVarsForSharedLibraryAtom(*shlibAtom); + } + for ( const AbsoluteAtom *absAtom : file.absolute() ) { + this->addIVarsForAbsoluteAtom(*absAtom); + } + + maybeConvertReferencesToV1(); + + // construct file header based on atom information accumulated + this->makeHeader(); + + std::error_code ec; + llvm::raw_fd_ostream out(outPath, ec, llvm::sys::fs::F_None); + if (ec) + return ec; + + this->write(out); + + return std::error_code(); + } + + virtual ~Writer() { + } + +private: + + // write the lld::File in native format to the specified stream + void write(raw_ostream &out) { + assert(out.tell() == 0); + out.write((char*)_headerBuffer, _headerBufferSize); + + writeChunk(out, _definedAtomIvars, NCS_DefinedAtomsV1); + writeChunk(out, _attributes, NCS_AttributesArrayV1); + writeChunk(out, _undefinedAtomIvars, NCS_UndefinedAtomsV1); + writeChunk(out, _sharedLibraryAtomIvars, NCS_SharedLibraryAtomsV1); + writeChunk(out, _absoluteAtomIvars, NCS_AbsoluteAtomsV1); + writeChunk(out, _absAttributes, NCS_AbsoluteAttributesV1); + writeChunk(out, _stringPool, NCS_Strings); + writeChunk(out, _referencesV1, NCS_ReferencesArrayV1); + writeChunk(out, _referencesV2, NCS_ReferencesArrayV2); + + if (!_targetsTableIndex.empty()) { + assert(out.tell() == findChunk(NCS_TargetsTable).fileOffset); + writeTargetTable(out); + } + + if (!_addendsTableIndex.empty()) { + assert(out.tell() == findChunk(NCS_AddendsTable).fileOffset); + writeAddendTable(out); + } + + writeChunk(out, _contentPool, NCS_Content); + } + + template<class T> + void writeChunk(raw_ostream &out, std::vector<T> &vector, uint32_t signature) { + if (vector.empty()) + return; + assert(out.tell() == findChunk(signature).fileOffset); + out.write((char*)&vector[0], vector.size() * sizeof(T)); + } + + void addIVarsForDefinedAtom(const DefinedAtom& atom) { + _definedAtomIndex[&atom] = _definedAtomIvars.size(); + NativeDefinedAtomIvarsV1 ivar; + unsigned refsCount; + ivar.nameOffset = getNameOffset(atom); + ivar.attributesOffset = getAttributeOffset(atom); + ivar.referencesStartIndex = getReferencesIndex(atom, refsCount); + ivar.referencesCount = refsCount; + ivar.contentOffset = getContentOffset(atom); + ivar.contentSize = atom.size(); + ivar.sectionSize = atom.sectionSize(); + _definedAtomIvars.push_back(ivar); + } + + void addIVarsForUndefinedAtom(const UndefinedAtom& atom) { + _undefinedAtomIndex[&atom] = _undefinedAtomIvars.size(); + NativeUndefinedAtomIvarsV1 ivar; + ivar.nameOffset = getNameOffset(atom); + ivar.flags = (atom.canBeNull() & 0x03); + ivar.fallbackNameOffset = 0; + if (atom.fallback()) + ivar.fallbackNameOffset = getNameOffset(*atom.fallback()); + _undefinedAtomIvars.push_back(ivar); + } + + void addIVarsForSharedLibraryAtom(const SharedLibraryAtom& atom) { + _sharedLibraryAtomIndex[&atom] = _sharedLibraryAtomIvars.size(); + NativeSharedLibraryAtomIvarsV1 ivar; + ivar.size = atom.size(); + ivar.nameOffset = getNameOffset(atom); + ivar.loadNameOffset = getSharedLibraryNameOffset(atom.loadName()); + ivar.type = (uint32_t)atom.type(); + ivar.flags = atom.canBeNullAtRuntime(); + _sharedLibraryAtomIvars.push_back(ivar); + } + + void addIVarsForAbsoluteAtom(const AbsoluteAtom& atom) { + _absoluteAtomIndex[&atom] = _absoluteAtomIvars.size(); + NativeAbsoluteAtomIvarsV1 ivar; + ivar.nameOffset = getNameOffset(atom); + ivar.attributesOffset = getAttributeOffset(atom); + ivar.reserved = 0; + ivar.value = atom.value(); + _absoluteAtomIvars.push_back(ivar); + } + + void convertReferencesToV1() { + for (const NativeReferenceIvarsV2 &v2 : _referencesV2) { + NativeReferenceIvarsV1 v1; + v1.offsetInAtom = v2.offsetInAtom; + v1.kindNamespace = v2.kindNamespace; + v1.kindArch = v2.kindArch; + v1.kindValue = v2.kindValue; + v1.targetIndex = (v2.targetIndex == NativeReferenceIvarsV2::noTarget) ? + (uint16_t)NativeReferenceIvarsV1::noTarget : v2.targetIndex; + v1.addendIndex = this->getAddendIndex(v2.addend); + _referencesV1.push_back(v1); + } + _referencesV2.clear(); + } + + bool canConvertReferenceToV1(const NativeReferenceIvarsV2 &ref) { + bool validOffset = (ref.offsetInAtom == NativeReferenceIvarsV2::noTarget) || + ref.offsetInAtom < NativeReferenceIvarsV1::noTarget; + return validOffset && ref.targetIndex < UINT16_MAX; + } + + // Convert vector of NativeReferenceIvarsV2 to NativeReferenceIvarsV1 if + // possible. + void maybeConvertReferencesToV1() { + std::set<int64_t> addends; + for (const NativeReferenceIvarsV2 &ref : _referencesV2) { + if (!canConvertReferenceToV1(ref)) + return; + addends.insert(ref.addend); + if (addends.size() >= UINT16_MAX) + return; + } + convertReferencesToV1(); + } + + // fill out native file header and chunk directory + void makeHeader() { + const bool hasDefines = !_definedAtomIvars.empty(); + const bool hasUndefines = !_undefinedAtomIvars.empty(); + const bool hasSharedLibraries = !_sharedLibraryAtomIvars.empty(); + const bool hasAbsolutes = !_absoluteAtomIvars.empty(); + const bool hasReferencesV1 = !_referencesV1.empty(); + const bool hasReferencesV2 = !_referencesV2.empty(); + const bool hasTargetsTable = !_targetsTableIndex.empty(); + const bool hasAddendTable = !_addendsTableIndex.empty(); + const bool hasContent = !_contentPool.empty(); + + int chunkCount = 1; // always have string pool chunk + if ( hasDefines ) chunkCount += 2; + if ( hasUndefines ) ++chunkCount; + if ( hasSharedLibraries ) ++chunkCount; + if ( hasAbsolutes ) chunkCount += 2; + if ( hasReferencesV1 ) ++chunkCount; + if ( hasReferencesV2 ) ++chunkCount; + if ( hasTargetsTable ) ++chunkCount; + if ( hasAddendTable ) ++chunkCount; + if ( hasContent ) ++chunkCount; + + _headerBufferSize = sizeof(NativeFileHeader) + + chunkCount*sizeof(NativeChunk); + _headerBuffer = reinterpret_cast<NativeFileHeader*> + (operator new(_headerBufferSize, std::nothrow)); + NativeChunk *chunks = + reinterpret_cast<NativeChunk*>(reinterpret_cast<char*>(_headerBuffer) + + sizeof(NativeFileHeader)); + memcpy(_headerBuffer->magic, NATIVE_FILE_HEADER_MAGIC, + sizeof(_headerBuffer->magic)); + _headerBuffer->endian = NFH_LittleEndian; + _headerBuffer->architecture = 0; + _headerBuffer->fileSize = 0; + _headerBuffer->chunkCount = chunkCount; + + // create chunk for defined atom ivar array + int nextIndex = 0; + uint32_t nextFileOffset = _headerBufferSize; + if (hasDefines) { + fillChunkHeader(chunks[nextIndex++], nextFileOffset, _definedAtomIvars, + NCS_DefinedAtomsV1); + + // create chunk for attributes + fillChunkHeader(chunks[nextIndex++], nextFileOffset, _attributes, + NCS_AttributesArrayV1); + } + + // create chunk for undefined atom array + if (hasUndefines) + fillChunkHeader(chunks[nextIndex++], nextFileOffset, _undefinedAtomIvars, + NCS_UndefinedAtomsV1); + + // create chunk for shared library atom array + if (hasSharedLibraries) + fillChunkHeader(chunks[nextIndex++], nextFileOffset, + _sharedLibraryAtomIvars, NCS_SharedLibraryAtomsV1); + + // create chunk for shared library atom array + if (hasAbsolutes) { + fillChunkHeader(chunks[nextIndex++], nextFileOffset, _absoluteAtomIvars, + NCS_AbsoluteAtomsV1); + + // create chunk for attributes + fillChunkHeader(chunks[nextIndex++], nextFileOffset, _absAttributes, + NCS_AbsoluteAttributesV1); + } + + // create chunk for symbol strings + // pad end of string pool to 4-bytes + while ((_stringPool.size() % 4) != 0) + _stringPool.push_back('\0'); + fillChunkHeader(chunks[nextIndex++], nextFileOffset, _stringPool, + NCS_Strings); + + // create chunk for referencesV2 + if (hasReferencesV1) + fillChunkHeader(chunks[nextIndex++], nextFileOffset, _referencesV1, + NCS_ReferencesArrayV1); + + // create chunk for referencesV2 + if (hasReferencesV2) + fillChunkHeader(chunks[nextIndex++], nextFileOffset, _referencesV2, + NCS_ReferencesArrayV2); + + // create chunk for target table + if (hasTargetsTable) { + NativeChunk& cht = chunks[nextIndex++]; + cht.signature = NCS_TargetsTable; + cht.fileOffset = nextFileOffset; + cht.fileSize = _targetsTableIndex.size() * sizeof(uint32_t); + cht.elementCount = _targetsTableIndex.size(); + nextFileOffset = cht.fileOffset + cht.fileSize; + } + + // create chunk for addend table + if (hasAddendTable) { + NativeChunk& chad = chunks[nextIndex++]; + chad.signature = NCS_AddendsTable; + chad.fileOffset = nextFileOffset; + chad.fileSize = _addendsTableIndex.size() * sizeof(Reference::Addend); + chad.elementCount = _addendsTableIndex.size(); + nextFileOffset = chad.fileOffset + chad.fileSize; + } + + // create chunk for content + if (hasContent) + fillChunkHeader(chunks[nextIndex++], nextFileOffset, _contentPool, + NCS_Content); + + _headerBuffer->fileSize = nextFileOffset; + } + + template<class T> + void fillChunkHeader(NativeChunk &chunk, uint32_t &nextFileOffset, + const std::vector<T> &data, uint32_t signature) { + chunk.signature = signature; + chunk.fileOffset = nextFileOffset; + chunk.fileSize = data.size() * sizeof(T); + chunk.elementCount = data.size(); + nextFileOffset = chunk.fileOffset + chunk.fileSize; + } + + // scan header to find particular chunk + NativeChunk& findChunk(uint32_t signature) { + const uint32_t chunkCount = _headerBuffer->chunkCount; + NativeChunk* chunks = + reinterpret_cast<NativeChunk*>(reinterpret_cast<char*>(_headerBuffer) + + sizeof(NativeFileHeader)); + for (uint32_t i=0; i < chunkCount; ++i) { + if ( chunks[i].signature == signature ) + return chunks[i]; + } + llvm_unreachable("findChunk() signature not found"); + } + + // append atom name to string pool and return offset + uint32_t getNameOffset(const Atom& atom) { + return this->getNameOffset(atom.name()); + } + + // check if name is already in pool or append and return offset + uint32_t getSharedLibraryNameOffset(StringRef name) { + assert(!name.empty()); + // look to see if this library name was used by another atom + for (auto &it : _sharedLibraryNames) + if (name.equals(it.first)) + return it.second; + // first use of this library name + uint32_t result = this->getNameOffset(name); + _sharedLibraryNames.push_back(std::make_pair(name, result)); + return result; + } + + // append atom name to string pool and return offset + uint32_t getNameOffset(StringRef name) { + if ( name.empty() ) + return 0; + uint32_t result = _stringPool.size(); + _stringPool.insert(_stringPool.end(), name.begin(), name.end()); + _stringPool.push_back(0); + return result; + } + + // append atom cotent to content pool and return offset + uint32_t getContentOffset(const DefinedAtom& atom) { + if (!atom.occupiesDiskSpace()) + return 0; + uint32_t result = _contentPool.size(); + ArrayRef<uint8_t> cont = atom.rawContent(); + _contentPool.insert(_contentPool.end(), cont.begin(), cont.end()); + return result; + } + + // reuse existing attributes entry or create a new one and return offet + uint32_t getAttributeOffset(const DefinedAtom& atom) { + NativeAtomAttributesV1 attrs = computeAttributesV1(atom); + return getOrPushAttribute(_attributes, attrs); + } + + uint32_t getAttributeOffset(const AbsoluteAtom& atom) { + NativeAtomAttributesV1 attrs = computeAbsoluteAttributes(atom); + return getOrPushAttribute(_absAttributes, attrs); + } + + uint32_t getOrPushAttribute(std::vector<NativeAtomAttributesV1> &dest, + const NativeAtomAttributesV1 &attrs) { + for (size_t i = 0, e = dest.size(); i < e; ++i) { + if (!memcmp(&dest[i], &attrs, sizeof(attrs))) { + // found that this set of attributes already used, so re-use + return i * sizeof(attrs); + } + } + // append new attribute set to end + uint32_t result = dest.size() * sizeof(attrs); + dest.push_back(attrs); + return result; + } + + uint32_t sectionNameOffset(const DefinedAtom& atom) { + // if section based on content, then no custom section name available + if (atom.sectionChoice() == DefinedAtom::sectionBasedOnContent) + return 0; + StringRef name = atom.customSectionName(); + assert(!name.empty()); + // look to see if this section name was used by another atom + for (auto &it : _sectionNames) + if (name.equals(it.first)) + return it.second; + // first use of this section name + uint32_t result = this->getNameOffset(name); + _sectionNames.push_back(std::make_pair(name, result)); + return result; + } + + NativeAtomAttributesV1 computeAttributesV1(const DefinedAtom& atom) { + NativeAtomAttributesV1 attrs; + attrs.sectionNameOffset = sectionNameOffset(atom); + attrs.align2 = atom.alignment().powerOf2; + attrs.alignModulus = atom.alignment().modulus; + attrs.scope = atom.scope(); + attrs.interposable = atom.interposable(); + attrs.merge = atom.merge(); + attrs.contentType = atom.contentType(); + attrs.sectionChoice = atom.sectionChoice(); + attrs.deadStrip = atom.deadStrip(); + attrs.dynamicExport = atom.dynamicExport(); + attrs.codeModel = atom.codeModel(); + attrs.permissions = atom.permissions(); + return attrs; + } + + NativeAtomAttributesV1 computeAbsoluteAttributes(const AbsoluteAtom& atom) { + NativeAtomAttributesV1 attrs; + attrs.scope = atom.scope(); + return attrs; + } + + // add references for this atom in a contiguous block in NCS_ReferencesArrayV2 + uint32_t getReferencesIndex(const DefinedAtom& atom, unsigned& refsCount) { + size_t startRefSize = _referencesV2.size(); + uint32_t result = startRefSize; + for (const Reference *ref : atom) { + NativeReferenceIvarsV2 nref; + nref.offsetInAtom = ref->offsetInAtom(); + nref.kindNamespace = (uint8_t)ref->kindNamespace(); + nref.kindArch = (uint8_t)ref->kindArch(); + nref.kindValue = ref->kindValue(); + nref.targetIndex = this->getTargetIndex(ref->target()); + nref.addend = ref->addend(); + nref.tag = ref->tag(); + _referencesV2.push_back(nref); + } + refsCount = _referencesV2.size() - startRefSize; + return (refsCount == 0) ? 0 : result; + } + + uint32_t getTargetIndex(const Atom* target) { + if ( target == nullptr ) + return NativeReferenceIvarsV2::noTarget; + TargetToIndex::const_iterator pos = _targetsTableIndex.find(target); + if ( pos != _targetsTableIndex.end() ) { + return pos->second; + } + uint32_t result = _targetsTableIndex.size(); + _targetsTableIndex[target] = result; + return result; + } + + void writeTargetTable(raw_ostream &out) { + // Build table of target indexes + uint32_t maxTargetIndex = _targetsTableIndex.size(); + assert(maxTargetIndex > 0); + std::vector<uint32_t> targetIndexes(maxTargetIndex); + for (auto &it : _targetsTableIndex) { + const Atom* atom = it.first; + uint32_t targetIndex = it.second; + assert(targetIndex < maxTargetIndex); + + TargetToIndex::iterator pos = _definedAtomIndex.find(atom); + if (pos != _definedAtomIndex.end()) { + targetIndexes[targetIndex] = pos->second; + continue; + } + uint32_t base = _definedAtomIvars.size(); + + pos = _undefinedAtomIndex.find(atom); + if (pos != _undefinedAtomIndex.end()) { + targetIndexes[targetIndex] = pos->second + base; + continue; + } + base += _undefinedAtomIndex.size(); + + pos = _sharedLibraryAtomIndex.find(atom); + if (pos != _sharedLibraryAtomIndex.end()) { + targetIndexes[targetIndex] = pos->second + base; + continue; + } + base += _sharedLibraryAtomIndex.size(); + + pos = _absoluteAtomIndex.find(atom); + assert(pos != _absoluteAtomIndex.end()); + targetIndexes[targetIndex] = pos->second + base; + } + // write table + out.write((char*)&targetIndexes[0], maxTargetIndex * sizeof(uint32_t)); + } + + uint32_t getAddendIndex(Reference::Addend addend) { + if ( addend == 0 ) + return 0; // addend index zero is used to mean "no addend" + AddendToIndex::const_iterator pos = _addendsTableIndex.find(addend); + if ( pos != _addendsTableIndex.end() ) { + return pos->second; + } + uint32_t result = _addendsTableIndex.size() + 1; // one-based index + _addendsTableIndex[addend] = result; + return result; + } + + void writeAddendTable(raw_ostream &out) { + // Build table of addends + uint32_t maxAddendIndex = _addendsTableIndex.size(); + std::vector<Reference::Addend> addends(maxAddendIndex); + for (auto &it : _addendsTableIndex) { + Reference::Addend addend = it.first; + uint32_t index = it.second; + assert(index <= maxAddendIndex); + addends[index-1] = addend; + } + // write table + out.write((char*)&addends[0], maxAddendIndex*sizeof(Reference::Addend)); + } + + typedef std::vector<std::pair<StringRef, uint32_t>> NameToOffsetVector; + + typedef llvm::DenseMap<const Atom*, uint32_t> TargetToIndex; + typedef llvm::DenseMap<Reference::Addend, uint32_t> AddendToIndex; + + NativeFileHeader* _headerBuffer; + size_t _headerBufferSize; + std::vector<char> _stringPool; + std::vector<uint8_t> _contentPool; + std::vector<NativeDefinedAtomIvarsV1> _definedAtomIvars; + std::vector<NativeAtomAttributesV1> _attributes; + std::vector<NativeAtomAttributesV1> _absAttributes; + std::vector<NativeUndefinedAtomIvarsV1> _undefinedAtomIvars; + std::vector<NativeSharedLibraryAtomIvarsV1> _sharedLibraryAtomIvars; + std::vector<NativeAbsoluteAtomIvarsV1> _absoluteAtomIvars; + std::vector<NativeReferenceIvarsV1> _referencesV1; + std::vector<NativeReferenceIvarsV2> _referencesV2; + TargetToIndex _targetsTableIndex; + TargetToIndex _definedAtomIndex; + TargetToIndex _undefinedAtomIndex; + TargetToIndex _sharedLibraryAtomIndex; + TargetToIndex _absoluteAtomIndex; + AddendToIndex _addendsTableIndex; + NameToOffsetVector _sectionNames; + NameToOffsetVector _sharedLibraryNames; +}; +} // end namespace native + +std::unique_ptr<Writer> createWriterNative() { + return std::unique_ptr<Writer>(new native::Writer()); +} +} // end namespace lld diff --git a/lib/ReaderWriter/PECOFF/Atoms.h b/lib/ReaderWriter/PECOFF/Atoms.h new file mode 100644 index 0000000000000..257edc17884b7 --- /dev/null +++ b/lib/ReaderWriter/PECOFF/Atoms.h @@ -0,0 +1,312 @@ +//===- lib/ReaderWriter/PECOFF/Atoms.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_PE_COFF_ATOMS_H +#define LLD_READER_WRITER_PE_COFF_ATOMS_H + +#include "lld/Core/File.h" +#include "lld/Core/Simple.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Object/COFF.h" +#include <vector> + +namespace lld { +namespace pecoff { +class COFFDefinedAtom; + +class COFFUndefinedAtom : public UndefinedAtom { +public: + COFFUndefinedAtom(const File &file, StringRef name, + const UndefinedAtom *fallback = nullptr) + : _owningFile(file), _name(name), _fallback(fallback) {} + + const File &file() const override { return _owningFile; } + StringRef name() const override { return _name; } + CanBeNull canBeNull() const override { return CanBeNull::canBeNullNever; } + const UndefinedAtom *fallback() const override { return _fallback; } + +private: + const File &_owningFile; + StringRef _name; + const UndefinedAtom *_fallback; +}; + +/// The base class of all COFF defined atoms. A derived class of +/// COFFBaseDefinedAtom may represent atoms read from a file or atoms created +/// by the linker. An example of the latter case is the jump table for symbols +/// in a DLL. +class COFFBaseDefinedAtom : public DefinedAtom { +public: + enum class Kind { + File, + Internal + }; + + const File &file() const override { return _file; } + StringRef name() const override { return _name; } + Interposable interposable() const override { return interposeNo; } + Merge merge() const override { return mergeNo; } + Alignment alignment() const override { return Alignment(0); } + StringRef customSectionName() const override { return ""; } + DeadStripKind deadStrip() const override { return deadStripNormal; } + + Kind getKind() const { return _kind; } + + void addReference(std::unique_ptr<SimpleReference> reference) { + _references.push_back(std::move(reference)); + } + + reference_iterator begin() const override { + return reference_iterator(*this, reinterpret_cast<const void *>(0)); + } + + reference_iterator end() const override { + return reference_iterator( + *this, reinterpret_cast<const void *>(_references.size())); + } + +protected: + COFFBaseDefinedAtom(const File &file, StringRef name, Kind kind) + : _file(file), _name(name), _kind(kind) {} + +private: + const Reference *derefIterator(const void *iter) const override { + size_t index = reinterpret_cast<size_t>(iter); + return _references[index].get(); + } + + void incrementIterator(const void *&iter) const override { + size_t index = reinterpret_cast<size_t>(iter); + iter = reinterpret_cast<const void *>(index + 1); + } + + const File &_file; + StringRef _name; + Kind _kind; + std::vector<std::unique_ptr<SimpleReference>> _references; +}; + +/// This is the root class of the atom read from a file. This class have two +/// subclasses; one for the regular atom and another for the BSS atom. +class COFFDefinedFileAtom : public COFFBaseDefinedAtom { +public: + COFFDefinedFileAtom(const File &file, StringRef name, StringRef sectionName, + uint64_t sectionSize, Scope scope, + ContentType contentType, ContentPermissions perms, + uint64_t ordinal) + : COFFBaseDefinedAtom(file, name, Kind::File), _sectionName(sectionName), + _sectionSize(sectionSize), _scope(scope), _contentType(contentType), + _permissions(perms), _ordinal(ordinal), _alignment(0) {} + + static bool classof(const COFFBaseDefinedAtom *atom) { + return atom->getKind() == Kind::File; + } + + void setAlignment(Alignment val) { _alignment = val; } + SectionChoice sectionChoice() const override { return sectionCustomRequired; } + StringRef customSectionName() const override { return _sectionName; } + uint64_t sectionSize() const override { return _sectionSize; } + Scope scope() const override { return _scope; } + ContentType contentType() const override { return _contentType; } + ContentPermissions permissions() const override { return _permissions; } + uint64_t ordinal() const override { return _ordinal; } + Alignment alignment() const override { return _alignment; } + + void addAssociate(const DefinedAtom *other) { + auto *ref = new SimpleReference(Reference::KindNamespace::all, + Reference::KindArch::all, + lld::Reference::kindAssociate, 0, other, 0); + addReference(std::unique_ptr<SimpleReference>(ref)); + } + +private: + StringRef _sectionName; + uint64_t _sectionSize; + Scope _scope; + ContentType _contentType; + ContentPermissions _permissions; + uint64_t _ordinal; + Alignment _alignment; + std::vector<std::unique_ptr<SimpleReference>> _references; +}; + +// A COFFDefinedAtom represents an atom read from a file and has contents. +class COFFDefinedAtom : public COFFDefinedFileAtom { +public: + COFFDefinedAtom(const File &file, StringRef name, StringRef sectionName, + uint64_t sectionSize, Scope scope, ContentType type, + bool isComdat, ContentPermissions perms, Merge merge, + ArrayRef<uint8_t> data, uint64_t ordinal) + : COFFDefinedFileAtom(file, name, sectionName, sectionSize, + scope, type, perms, ordinal), + _isComdat(isComdat), _merge(merge), _dataref(data) {} + + Merge merge() const override { return _merge; } + uint64_t size() const override { return _dataref.size(); } + ArrayRef<uint8_t> rawContent() const override { return _dataref; } + + DeadStripKind deadStrip() const override { + // Only COMDAT symbols would be dead-stripped. + return _isComdat ? deadStripNormal : deadStripNever; + } + +private: + bool _isComdat; + Merge _merge; + ArrayRef<uint8_t> _dataref; +}; + +// A COFFDefinedAtom represents an atom for BSS section. +class COFFBSSAtom : public COFFDefinedFileAtom { +public: + COFFBSSAtom(const File &file, StringRef name, Scope scope, + ContentPermissions perms, Merge merge, uint32_t size, + uint64_t ordinal) + : COFFDefinedFileAtom(file, name, ".bss", 0, scope, typeZeroFill, + perms, ordinal), + _merge(merge), _size(size) {} + + Merge merge() const override { return _merge; } + uint64_t size() const override { return _size; } + ArrayRef<uint8_t> rawContent() const override { return _contents; } + +private: + Merge _merge; + uint32_t _size; + std::vector<uint8_t> _contents; +}; + +/// A COFFLinkerInternalAtom represents a defined atom created by the linker, +/// not read from file. +class COFFLinkerInternalAtom : public COFFBaseDefinedAtom { +public: + SectionChoice sectionChoice() const override { return sectionBasedOnContent; } + uint64_t ordinal() const override { return _ordinal; } + Scope scope() const override { return scopeGlobal; } + Alignment alignment() const override { return Alignment(0); } + uint64_t size() const override { return _data.size(); } + ArrayRef<uint8_t> rawContent() const override { return _data; } + +protected: + COFFLinkerInternalAtom(const File &file, uint64_t ordinal, + std::vector<uint8_t> data, StringRef symbolName = "") + : COFFBaseDefinedAtom(file, symbolName, Kind::Internal), + _ordinal(ordinal), _data(std::move(data)) {} + +private: + uint64_t _ordinal; + std::vector<uint8_t> _data; +}; + +class COFFStringAtom : public COFFLinkerInternalAtom { +public: + COFFStringAtom(const File &file, uint64_t ordinal, StringRef sectionName, + StringRef contents) + : COFFLinkerInternalAtom(file, ordinal, stringRefToVector(contents)), + _sectionName(sectionName) {} + + SectionChoice sectionChoice() const override { return sectionCustomRequired; } + StringRef customSectionName() const override { return _sectionName; } + ContentType contentType() const override { return typeData; } + ContentPermissions permissions() const override { return permR__; } + +private: + StringRef _sectionName; + + std::vector<uint8_t> stringRefToVector(StringRef name) const { + std::vector<uint8_t> ret(name.size() + 1); + memcpy(&ret[0], name.data(), name.size()); + ret[name.size()] = 0; + return ret; + } +}; + +// A COFFSharedLibraryAtom represents a symbol for data in an import library. A +// reference to a COFFSharedLibraryAtom will be transformed to a real reference +// to an import address table entry in Idata pass. +class COFFSharedLibraryAtom : public SharedLibraryAtom { +public: + COFFSharedLibraryAtom(const File &file, uint16_t hint, StringRef symbolName, + StringRef importName, StringRef dllName) + : _file(file), _hint(hint), _mangledName(addImpPrefix(symbolName)), + _importName(importName), _dllName(dllName), _importTableEntry(nullptr) { + } + + const File &file() const override { return _file; } + uint16_t hint() const { return _hint; } + + /// Returns the symbol name to be used by the core linker. + StringRef name() const override { return _mangledName; } + + /// Returns the symbol name to be used in the import description table in the + /// COFF header. + virtual StringRef importName() const { return _importName; } + + StringRef loadName() const override { return _dllName; } + bool canBeNullAtRuntime() const override { return false; } + Type type() const override { return Type::Unknown; } + uint64_t size() const override { return 0; } + + void setImportTableEntry(const DefinedAtom *atom) { + _importTableEntry = atom; + } + + const DefinedAtom *getImportTableEntry() const { return _importTableEntry; } + +private: + /// Mangle the symbol name by adding "__imp_" prefix. See the file comment of + /// ReaderImportHeader.cpp for details about the prefix. + std::string addImpPrefix(StringRef symbolName) { + std::string ret("__imp_"); + ret.append(symbolName); + return ret; + } + + const File &_file; + uint16_t _hint; + std::string _mangledName; + std::string _importName; + StringRef _dllName; + const DefinedAtom *_importTableEntry; +}; + +// An instance of this class represents "input file" for atoms created in a +// pass. Atoms need to be associated to an input file even if it's not read from +// a file, so we use this class for that. +class VirtualFile : public SimpleFile { +public: + VirtualFile(const LinkingContext &ctx) + : SimpleFile("<virtual-file>"), _nextOrdinal(0) { + setOrdinal(ctx.getNextOrdinalAndIncrement()); + } + + uint64_t getNextOrdinal() { return _nextOrdinal++; } + +private: + uint64_t _nextOrdinal; +}; + +//===----------------------------------------------------------------------===// +// +// Utility functions to handle layout edges. +// +//===----------------------------------------------------------------------===// + +template <typename T, typename U> +void addLayoutEdge(T *a, U *b, uint32_t which) { + auto ref = new SimpleReference(Reference::KindNamespace::all, + Reference::KindArch::all, + which, 0, b, 0); + a->addReference(std::unique_ptr<SimpleReference>(ref)); +} + +} // namespace pecoff +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/PECOFF/CMakeLists.txt b/lib/ReaderWriter/PECOFF/CMakeLists.txt new file mode 100644 index 0000000000000..86b49b79f194e --- /dev/null +++ b/lib/ReaderWriter/PECOFF/CMakeLists.txt @@ -0,0 +1,16 @@ +add_llvm_library(lldPECOFF + EdataPass.cpp + IdataPass.cpp + LinkerGeneratedSymbolFile.cpp + LoadConfigPass.cpp + PECOFFLinkingContext.cpp + Pass.cpp + ReaderCOFF.cpp + ReaderImportHeader.cpp + WriterImportLibrary.cpp + WriterPECOFF.cpp + LINK_LIBS + lldCore + LLVMObject + LLVMSupport + ) diff --git a/lib/ReaderWriter/PECOFF/EdataPass.cpp b/lib/ReaderWriter/PECOFF/EdataPass.cpp new file mode 100644 index 0000000000000..ad79f171f3c9f --- /dev/null +++ b/lib/ReaderWriter/PECOFF/EdataPass.cpp @@ -0,0 +1,227 @@ +//===- lib/ReaderWriter/PECOFF/EdataPass.cpp ------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Pass.h" +#include "EdataPass.h" +#include "lld/Core/File.h" +#include "lld/Core/Pass.h" +#include "lld/Core/Simple.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Path.h" +#include <climits> +#include <ctime> +#include <utility> + +using lld::pecoff::edata::EdataAtom; +using lld::pecoff::edata::TableEntry; +using llvm::object::export_address_table_entry; +using llvm::object::export_directory_table_entry; + +namespace lld { +namespace pecoff { + +typedef PECOFFLinkingContext::ExportDesc ExportDesc; + +// dedupExports removes duplicate export entries. If two exports are +// referring the same symbol, they are considered duplicates. +// This could happen if the same symbol name is specified as an argument +// to /export more than once, or an unmangled and mangled name of the +// same symbol are given to /export. In the latter case, we choose +// unmangled (shorter) name. +static void dedupExports(PECOFFLinkingContext &ctx) { + std::vector<ExportDesc> &exports = ctx.getDllExports(); + // Pass 1: find duplicate entries + std::set<const ExportDesc *> dup; + std::map<StringRef, ExportDesc *> map; + for (ExportDesc &exp : exports) { + if (!exp.externalName.empty()) + continue; + StringRef symbol = exp.getRealName(); + auto it = map.find(symbol); + if (it == map.end()) { + map[symbol] = &exp; + } else if (symbol.size() < it->second->getRealName().size()) { + map[symbol] = &exp; + dup.insert(it->second); + } else { + dup.insert(&exp); + } + } + // Pass 2: remove duplicate entries + auto pred = [&](const ExportDesc &exp) { + return dup.count(&exp) == 1; + }; + exports.erase(std::remove_if(exports.begin(), exports.end(), pred), + exports.end()); +} + +static void assignOrdinals(PECOFFLinkingContext &ctx) { + std::vector<ExportDesc> &exports = ctx.getDllExports(); + int maxOrdinal = -1; + for (ExportDesc &desc : exports) + maxOrdinal = std::max(maxOrdinal, desc.ordinal); + + std::sort(exports.begin(), exports.end(), + [](const ExportDesc &a, const ExportDesc &b) { + return a.getExternalName().compare(b.getExternalName()) < 0; + }); + + int nextOrdinal = (maxOrdinal == -1) ? 1 : (maxOrdinal + 1); + for (ExportDesc &desc : exports) + if (desc.ordinal == -1) + desc.ordinal = nextOrdinal++; +} + +static bool getExportedAtoms(PECOFFLinkingContext &ctx, MutableFile *file, + std::vector<TableEntry> &ret) { + std::map<StringRef, const DefinedAtom *> definedAtoms; + for (const DefinedAtom *atom : file->defined()) + definedAtoms[atom->name()] = atom; + + for (PECOFFLinkingContext::ExportDesc &desc : ctx.getDllExports()) { + auto it = definedAtoms.find(desc.getRealName()); + if (it == definedAtoms.end()) { + llvm::errs() << "Symbol <" << desc.name + << "> is exported but not defined.\n"; + return false; + } + const DefinedAtom *atom = it->second; + + // One can export a symbol with a different name than the symbol + // name used in DLL. If such name is specified, use it in the + // .edata section. + ret.push_back(TableEntry(ctx.undecorateSymbol(desc.getExternalName()), + desc.ordinal, atom, desc.noname)); + } + std::sort(ret.begin(), ret.end(), + [](const TableEntry &a, const TableEntry &b) { + return a.exportName.compare(b.exportName) < 0; + }); + + return true; +} + +static std::pair<int, int> getOrdinalBase(std::vector<TableEntry> &entries) { + int ordinalBase = INT_MAX; + int maxOrdinal = -1; + for (TableEntry &e : entries) { + ordinalBase = std::min(ordinalBase, e.ordinal); + maxOrdinal = std::max(maxOrdinal, e.ordinal); + } + return std::pair<int, int>(ordinalBase, maxOrdinal); +} + +edata::EdataAtom * +EdataPass::createAddressTable(const std::vector<TableEntry> &entries, + int ordinalBase, int maxOrdinal) { + EdataAtom *addressTable = + new (_alloc) EdataAtom(_file, sizeof(export_address_table_entry) * + (maxOrdinal - ordinalBase + 1)); + + for (const TableEntry &e : entries) { + int index = e.ordinal - ordinalBase; + size_t offset = index * sizeof(export_address_table_entry); + addDir32NBReloc(addressTable, e.atom, _ctx.getMachineType(), offset); + } + return addressTable; +} + +edata::EdataAtom * +EdataPass::createNamePointerTable(const PECOFFLinkingContext &ctx, + const std::vector<TableEntry> &entries, + MutableFile *file) { + EdataAtom *table = + new (_alloc) EdataAtom(_file, sizeof(uint32_t) * entries.size()); + + size_t offset = 0; + for (const TableEntry &e : entries) { + auto *stringAtom = new (_alloc) COFFStringAtom( + _file, _stringOrdinal++, ".edata", e.exportName); + file->addAtom(*stringAtom); + addDir32NBReloc(table, stringAtom, _ctx.getMachineType(), offset); + offset += sizeof(uint32_t); + } + return table; +} + +edata::EdataAtom *EdataPass::createExportDirectoryTable( + const std::vector<edata::TableEntry> &namedEntries, int ordinalBase, + int maxOrdinal) { + EdataAtom *ret = + new (_alloc) EdataAtom(_file, sizeof(export_directory_table_entry)); + auto *data = ret->getContents<export_directory_table_entry>(); + data->TimeDateStamp = time(nullptr); + data->OrdinalBase = ordinalBase; + data->AddressTableEntries = maxOrdinal - ordinalBase + 1; + data->NumberOfNamePointers = namedEntries.size(); + return ret; +} + +edata::EdataAtom * +EdataPass::createOrdinalTable(const std::vector<TableEntry> &entries, + int ordinalBase) { + EdataAtom *ret = + new (_alloc) EdataAtom(_file, sizeof(uint16_t) * entries.size()); + uint16_t *data = ret->getContents<uint16_t>(); + int i = 0; + for (const TableEntry &e : entries) + data[i++] = e.ordinal - ordinalBase; + return ret; +} + +void EdataPass::perform(std::unique_ptr<MutableFile> &file) { + dedupExports(_ctx); + assignOrdinals(_ctx); + + std::vector<TableEntry> entries; + if (!getExportedAtoms(_ctx, file.get(), entries)) + return; + if (entries.empty()) + return; + + int ordinalBase, maxOrdinal; + std::tie(ordinalBase, maxOrdinal) = getOrdinalBase(entries); + + std::vector<TableEntry> namedEntries; + for (TableEntry &e : entries) + if (!e.noname) + namedEntries.push_back(e); + + EdataAtom *table = + createExportDirectoryTable(namedEntries, ordinalBase, maxOrdinal); + file->addAtom(*table); + + COFFStringAtom *dllName = + new (_alloc) COFFStringAtom(_file, _stringOrdinal++, ".edata", + llvm::sys::path::filename(_ctx.outputPath())); + file->addAtom(*dllName); + addDir32NBReloc(table, dllName, _ctx.getMachineType(), + offsetof(export_directory_table_entry, NameRVA)); + + EdataAtom *addressTable = + createAddressTable(entries, ordinalBase, maxOrdinal); + file->addAtom(*addressTable); + addDir32NBReloc( + table, addressTable, _ctx.getMachineType(), + offsetof(export_directory_table_entry, ExportAddressTableRVA)); + + EdataAtom *namePointerTable = + createNamePointerTable(_ctx, namedEntries, file.get()); + file->addAtom(*namePointerTable); + addDir32NBReloc(table, namePointerTable, _ctx.getMachineType(), + offsetof(export_directory_table_entry, NamePointerRVA)); + + EdataAtom *ordinalTable = createOrdinalTable(namedEntries, ordinalBase); + file->addAtom(*ordinalTable); + addDir32NBReloc(table, ordinalTable, _ctx.getMachineType(), + offsetof(export_directory_table_entry, OrdinalTableRVA)); +} + +} // namespace pecoff +} // namespace lld diff --git a/lib/ReaderWriter/PECOFF/EdataPass.h b/lib/ReaderWriter/PECOFF/EdataPass.h new file mode 100644 index 0000000000000..442be3ca24aa0 --- /dev/null +++ b/lib/ReaderWriter/PECOFF/EdataPass.h @@ -0,0 +1,99 @@ +//===- lib/ReaderWriter/PECOFF/EdataPass.h --------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file \brief This linker pass creates atoms for the DLL export +/// information. The defined atoms constructed in this pass will go into .edata +/// section. +/// +/// For the details of the .edata section format, see Microsoft PE/COFF +/// Specification section 5.3, The .edata Section. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_PE_COFF_EDATA_PASS_H +#define LLD_READER_WRITER_PE_COFF_EDATA_PASS_H + +#include "Atoms.h" +#include "lld/Core/File.h" +#include "lld/Core/Pass.h" +#include "lld/Core/Simple.h" +#include "lld/ReaderWriter/PECOFFLinkingContext.h" +#include "llvm/Support/COFF.h" +#include <map> + +using llvm::COFF::ImportDirectoryTableEntry; + +namespace lld { +namespace pecoff { +namespace edata { + +struct TableEntry { + TableEntry(StringRef exp, int ord, const DefinedAtom *a, bool n) + : exportName(exp), ordinal(ord), atom(a), noname(n) {} + std::string exportName; + int ordinal; + const DefinedAtom *atom; + bool noname; +}; + +/// The root class of all edata atoms. +class EdataAtom : public COFFLinkerInternalAtom { +public: + EdataAtom(VirtualFile &file, size_t size) + : COFFLinkerInternalAtom(file, file.getNextOrdinal(), + std::vector<uint8_t>(size)) {} + + SectionChoice sectionChoice() const override { return sectionCustomRequired; } + StringRef customSectionName() const override { return ".edata"; } + ContentType contentType() const override { return typeData; } + ContentPermissions permissions() const override { return permR__; } + + template <typename T> T *getContents() const { + return (T *)const_cast<uint8_t *>(rawContent().data()); + } +}; + +} // namespace edata + +class EdataPass : public lld::Pass { +public: + EdataPass(PECOFFLinkingContext &ctx) + : _ctx(ctx), _file(ctx), _is64(ctx.is64Bit()), _stringOrdinal(1024) {} + + void perform(std::unique_ptr<MutableFile> &file) override; + +private: + edata::EdataAtom * + createExportDirectoryTable(const std::vector<edata::TableEntry> &namedEntries, + int ordinalBase, int maxOrdinal); + + edata::EdataAtom * + createAddressTable(const std::vector<edata::TableEntry> &entries, + int ordinalBase, int maxOrdinal); + + edata::EdataAtom * + createNamePointerTable(const PECOFFLinkingContext &ctx, + const std::vector<edata::TableEntry> &entries, + MutableFile *file); + + edata::EdataAtom * + createOrdinalTable(const std::vector<edata::TableEntry> &entries, + int ordinalBase); + + PECOFFLinkingContext &_ctx; + VirtualFile _file; + bool _is64; + int _stringOrdinal; + mutable llvm::BumpPtrAllocator _alloc; +}; + +} // namespace pecoff +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/PECOFF/IdataPass.cpp b/lib/ReaderWriter/PECOFF/IdataPass.cpp new file mode 100644 index 0000000000000..d41ef581f7fa5 --- /dev/null +++ b/lib/ReaderWriter/PECOFF/IdataPass.cpp @@ -0,0 +1,345 @@ +//===- lib/ReaderWriter/PECOFF/IdataPass.cpp ------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "IdataPass.h" +#include "Pass.h" +#include "lld/Core/File.h" +#include "lld/Core/Pass.h" +#include "lld/Core/Simple.h" +#include "llvm/Support/COFF.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Endian.h" +#include <algorithm> +#include <cstddef> +#include <cstring> +#include <map> +#include <vector> + +using namespace llvm::support::endian; +using llvm::object::delay_import_directory_table_entry; + +namespace lld { +namespace pecoff { +namespace idata { + +IdataAtom::IdataAtom(IdataContext &context, std::vector<uint8_t> data) + : COFFLinkerInternalAtom(context.dummyFile, + context.dummyFile.getNextOrdinal(), data) { + context.file.addAtom(*this); +} + +HintNameAtom::HintNameAtom(IdataContext &context, uint16_t hint, + StringRef importName) + : IdataAtom(context, assembleRawContent(hint, importName)), + _importName(importName) {} + +std::vector<uint8_t> HintNameAtom::assembleRawContent(uint16_t hint, + StringRef importName) { + size_t size = + llvm::RoundUpToAlignment(sizeof(hint) + importName.size() + 1, 2); + std::vector<uint8_t> ret(size); + ret[importName.size()] = 0; + ret[importName.size() - 1] = 0; + write16le(&ret[0], hint); + std::memcpy(&ret[2], importName.data(), importName.size()); + return ret; +} + +std::vector<uint8_t> +ImportTableEntryAtom::assembleRawContent(uint64_t rva, bool is64) { + // The element size of the import table is 32 bit in PE and 64 bit + // in PE+. In PE+, bits 62-31 are filled with zero. + if (is64) { + std::vector<uint8_t> ret(8); + write64le(&ret[0], rva); + return ret; + } + std::vector<uint8_t> ret(4); + write32le(&ret[0], rva); + return ret; +} + +static std::vector<ImportTableEntryAtom *> +createImportTableAtoms(IdataContext &context, + const std::vector<COFFSharedLibraryAtom *> &sharedAtoms, + bool shouldAddReference, StringRef sectionName, + llvm::BumpPtrAllocator &alloc) { + std::vector<ImportTableEntryAtom *> ret; + for (COFFSharedLibraryAtom *atom : sharedAtoms) { + ImportTableEntryAtom *entry = nullptr; + if (atom->importName().empty()) { + // Import by ordinal + uint64_t hint = atom->hint(); + hint |= context.ctx.is64Bit() ? (uint64_t(1) << 63) : (uint64_t(1) << 31); + entry = new (alloc) ImportTableEntryAtom(context, hint, sectionName); + } else { + // Import by name + entry = new (alloc) ImportTableEntryAtom(context, 0, sectionName); + HintNameAtom *hintName = + new (alloc) HintNameAtom(context, atom->hint(), atom->importName()); + addDir32NBReloc(entry, hintName, context.ctx.getMachineType(), 0); + } + ret.push_back(entry); + if (shouldAddReference) + atom->setImportTableEntry(entry); + } + // Add the NULL entry. + ret.push_back(new (alloc) ImportTableEntryAtom(context, 0, sectionName)); + return ret; +} + +// Creates atoms for an import lookup table. The import lookup table is an +// array of pointers to hint/name atoms. The array needs to be terminated with +// the NULL entry. +void ImportDirectoryAtom::addRelocations( + IdataContext &context, StringRef loadName, + const std::vector<COFFSharedLibraryAtom *> &sharedAtoms) { + // Create parallel arrays. The contents of the two are initially the + // same. The PE/COFF loader overwrites the import address tables with the + // pointers to the referenced items after loading the executable into + // memory. + std::vector<ImportTableEntryAtom *> importLookupTables = + createImportTableAtoms(context, sharedAtoms, false, ".idata.t", _alloc); + std::vector<ImportTableEntryAtom *> importAddressTables = + createImportTableAtoms(context, sharedAtoms, true, ".idata.a", _alloc); + + addDir32NBReloc(this, importLookupTables[0], context.ctx.getMachineType(), + offsetof(ImportDirectoryTableEntry, ImportLookupTableRVA)); + addDir32NBReloc(this, importAddressTables[0], context.ctx.getMachineType(), + offsetof(ImportDirectoryTableEntry, ImportAddressTableRVA)); + auto *atom = new (_alloc) + COFFStringAtom(context.dummyFile, context.dummyFile.getNextOrdinal(), + ".idata", loadName); + context.file.addAtom(*atom); + addDir32NBReloc(this, atom, context.ctx.getMachineType(), + offsetof(ImportDirectoryTableEntry, NameRVA)); +} + +// Create the contents for the delay-import table. +std::vector<uint8_t> DelayImportDirectoryAtom::createContent() { + std::vector<uint8_t> r(sizeof(delay_import_directory_table_entry), 0); + auto entry = reinterpret_cast<delay_import_directory_table_entry *>(&r[0]); + // link.exe seems to set 1 to Attributes field, so do we. + entry->Attributes = 1; + return r; +} + +// Find "___delayLoadHelper2@8" (or "__delayLoadHelper2" on x64). +// This is not efficient but should be OK for now. +static const Atom * +findDelayLoadHelper(MutableFile &file, const PECOFFLinkingContext &ctx) { + StringRef sym = ctx.getDelayLoadHelperName(); + for (const DefinedAtom *atom : file.defined()) + if (atom->name() == sym) + return atom; + std::string msg = (sym + " was not found").str(); + llvm_unreachable(msg.c_str()); +} + +// Create the data referred by the delay-import table. +void DelayImportDirectoryAtom::addRelocations( + IdataContext &context, StringRef loadName, + const std::vector<COFFSharedLibraryAtom *> &sharedAtoms) { + // "ModuleHandle" field. This points to an array of pointer-size data + // in ".data" section. Initially the array is initialized with zero. + // The delay-load import helper will set DLL base address at runtime. + auto *hmodule = new (_alloc) DelayImportAddressAtom(context); + addDir32NBReloc(this, hmodule, context.ctx.getMachineType(), + offsetof(delay_import_directory_table_entry, ModuleHandle)); + + // "NameTable" field. The data structure of this field is the same + // as (non-delay) import table's Import Lookup Table. Contains + // imported function names. This is a parallel array of AddressTable + // field. + std::vector<ImportTableEntryAtom *> nameTable = + createImportTableAtoms(context, sharedAtoms, false, ".didat", _alloc); + addDir32NBReloc( + this, nameTable[0], context.ctx.getMachineType(), + offsetof(delay_import_directory_table_entry, DelayImportNameTable)); + + // "Name" field. This points to the NUL-terminated DLL name string. + auto *name = new (_alloc) + COFFStringAtom(context.dummyFile, context.dummyFile.getNextOrdinal(), + ".didat", loadName); + context.file.addAtom(*name); + addDir32NBReloc(this, name, context.ctx.getMachineType(), + offsetof(delay_import_directory_table_entry, Name)); + + // "AddressTable" field. This points to an array of pointers, which + // in turn pointing to delay-load functions. + std::vector<DelayImportAddressAtom *> addrTable; + for (int i = 0, e = sharedAtoms.size() + 1; i < e; ++i) + addrTable.push_back(new (_alloc) DelayImportAddressAtom(context)); + for (int i = 0, e = sharedAtoms.size(); i < e; ++i) + sharedAtoms[i]->setImportTableEntry(addrTable[i]); + addDir32NBReloc( + this, addrTable[0], context.ctx.getMachineType(), + offsetof(delay_import_directory_table_entry, DelayImportAddressTable)); + + const Atom *delayLoadHelper = findDelayLoadHelper(context.file, context.ctx); + for (int i = 0, e = sharedAtoms.size(); i < e; ++i) { + const DefinedAtom *loader = new (_alloc) DelayLoaderAtom( + context, addrTable[i], this, delayLoadHelper); + if (context.ctx.is64Bit()) + addDir64Reloc(addrTable[i], loader, context.ctx.getMachineType(), 0); + else + addDir32Reloc(addrTable[i], loader, context.ctx.getMachineType(), 0); + } +} + +DelayLoaderAtom::DelayLoaderAtom(IdataContext &context, const Atom *impAtom, + const Atom *descAtom, const Atom *delayLoadHelperAtom) + : IdataAtom(context, createContent(context.ctx.getMachineType())) { + MachineTypes machine = context.ctx.getMachineType(); + switch (machine) { + case llvm::COFF::IMAGE_FILE_MACHINE_I386: + addDir32Reloc(this, impAtom, machine, 3); + addDir32Reloc(this, descAtom, machine, 8); + addRel32Reloc(this, delayLoadHelperAtom, machine, 13); + break; + case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: + addRel32Reloc(this, impAtom, machine, 36); + addRel32Reloc(this, descAtom, machine, 43); + addRel32Reloc(this, delayLoadHelperAtom, machine, 48); + break; + default: + llvm::report_fatal_error("unsupported machine type"); + } +} + +// DelayLoaderAtom contains a wrapper function for __delayLoadHelper2. +// +// __delayLoadHelper2 takes two pointers: a pointer to the delay-load +// table descripter and a pointer to _imp_ symbol for the function +// to be resolved. +// +// __delayLoadHelper2 looks at the table descriptor to know the DLL +// name, calls dlopen()-like function to load it, resolves all +// imported symbols, and then writes the resolved addresses to the +// import address table. It returns a pointer to the resolved +// function. +// +// __delayLoadHelper2 is defined in delayimp.lib. +std::vector<uint8_t> +DelayLoaderAtom::createContent(MachineTypes machine) const { + static const uint8_t x86[] = { + 0x51, // push ecx + 0x52, // push edx + 0x68, 0, 0, 0, 0, // push offset ___imp__<FUNCNAME> + 0x68, 0, 0, 0, 0, // push offset ___DELAY_IMPORT_DESCRIPTOR_<DLLNAME>_dll + 0xE8, 0, 0, 0, 0, // call ___delayLoadHelper2@8 + 0x5A, // pop edx + 0x59, // pop ecx + 0xFF, 0xE0, // jmp eax + }; + static const uint8_t x64[] = { + 0x51, // push rcx + 0x52, // push rdx + 0x41, 0x50, // push r8 + 0x41, 0x51, // push r9 + 0x48, 0x83, 0xEC, 0x48, // sub rsp, 48h + 0x66, 0x0F, 0x7F, 0x04, 0x24, // movdqa xmmword ptr [rsp], xmm0 + 0x66, 0x0F, 0x7F, 0x4C, 0x24, 0x10, // movdqa xmmword ptr [rsp+10h], xmm1 + 0x66, 0x0F, 0x7F, 0x54, 0x24, 0x20, // movdqa xmmword ptr [rsp+20h], xmm2 + 0x66, 0x0F, 0x7F, 0x5C, 0x24, 0x30, // movdqa xmmword ptr [rsp+30h], xmm3 + 0x48, 0x8D, 0x15, 0, 0, 0, 0, // lea rdx, [__imp_<FUNCNAME>] + 0x48, 0x8D, 0x0D, 0, 0, 0, 0, // lea rcx, [___DELAY_IMPORT_...] + 0xE8, 0, 0, 0, 0, // call __delayLoadHelper2 + 0x66, 0x0F, 0x6F, 0x04, 0x24, // movdqa xmm0, xmmword ptr [rsp] + 0x66, 0x0F, 0x6F, 0x4C, 0x24, 0x10, // movdqa xmm1, xmmword ptr [rsp+10h] + 0x66, 0x0F, 0x6F, 0x54, 0x24, 0x20, // movdqa xmm2, xmmword ptr [rsp+20h] + 0x66, 0x0F, 0x6F, 0x5C, 0x24, 0x30, // movdqa xmm3, xmmword ptr [rsp+30h] + 0x48, 0x83, 0xC4, 0x48, // add rsp, 48h + 0x41, 0x59, // pop r9 + 0x41, 0x58, // pop r8 + 0x5A, // pop rdx + 0x59, // pop rcx + 0xFF, 0xE0, // jmp rax + }; + switch (machine) { + case llvm::COFF::IMAGE_FILE_MACHINE_I386: + return std::vector<uint8_t>(x86, x86 + sizeof(x86)); + case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: + return std::vector<uint8_t>(x64, x64 + sizeof(x64)); + default: + llvm::report_fatal_error("unsupported machine type"); + } +} + +} // namespace idata + +void IdataPass::perform(std::unique_ptr<MutableFile> &file) { + if (file->sharedLibrary().empty()) + return; + + idata::IdataContext context(*file, _dummyFile, _ctx); + std::map<StringRef, std::vector<COFFSharedLibraryAtom *>> sharedAtoms = + groupByLoadName(*file); + bool hasImports = false; + bool hasDelayImports = false; + + // Create the import table and terminate it with the null entry. + for (auto i : sharedAtoms) { + StringRef loadName = i.first; + if (_ctx.isDelayLoadDLL(loadName)) + continue; + hasImports = true; + std::vector<COFFSharedLibraryAtom *> &atoms = i.second; + new (_alloc) idata::ImportDirectoryAtom(context, loadName, atoms); + } + if (hasImports) + new (_alloc) idata::NullImportDirectoryAtom(context); + + // Create the delay import table and terminate it with the null entry. + for (auto i : sharedAtoms) { + StringRef loadName = i.first; + if (!_ctx.isDelayLoadDLL(loadName)) + continue; + hasDelayImports = true; + std::vector<COFFSharedLibraryAtom *> &atoms = i.second; + new (_alloc) idata::DelayImportDirectoryAtom(context, loadName, atoms); + } + if (hasDelayImports) + new (_alloc) idata::DelayNullImportDirectoryAtom(context); + + replaceSharedLibraryAtoms(*file); +} + +std::map<StringRef, std::vector<COFFSharedLibraryAtom *> > +IdataPass::groupByLoadName(MutableFile &file) { + std::map<StringRef, COFFSharedLibraryAtom *> uniqueAtoms; + for (const SharedLibraryAtom *atom : file.sharedLibrary()) + uniqueAtoms[atom->name()] = + (COFFSharedLibraryAtom *)const_cast<SharedLibraryAtom *>(atom); + + std::map<StringRef, std::vector<COFFSharedLibraryAtom *> > ret; + for (auto i : uniqueAtoms) { + COFFSharedLibraryAtom *atom = i.second; + ret[atom->loadName()].push_back(atom); + } + return ret; +} + +/// Transforms a reference to a COFFSharedLibraryAtom to a real reference. +void IdataPass::replaceSharedLibraryAtoms(MutableFile &file) { + for (const DefinedAtom *atom : file.defined()) { + for (const Reference *ref : *atom) { + const Atom *target = ref->target(); + auto *sharedAtom = dyn_cast<SharedLibraryAtom>(target); + if (!sharedAtom) + continue; + const auto *coffSharedAtom = (const COFFSharedLibraryAtom *)sharedAtom; + const DefinedAtom *entry = coffSharedAtom->getImportTableEntry(); + const_cast<Reference *>(ref)->setTarget(entry); + } + } +} + +} // namespace pecoff +} // namespace lld diff --git a/lib/ReaderWriter/PECOFF/IdataPass.h b/lib/ReaderWriter/PECOFF/IdataPass.h new file mode 100644 index 0000000000000..9db82160339a3 --- /dev/null +++ b/lib/ReaderWriter/PECOFF/IdataPass.h @@ -0,0 +1,218 @@ +//===- lib/ReaderWriter/PECOFF/IdataPass.h---------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file \brief This linker pass creates atoms for the DLL import +/// information. The defined atoms constructed in this pass will go into .idata +/// section, unless .idata section is merged with other section such as .data. +/// +/// For the details of the .idata section format, see Microsoft PE/COFF +/// Specification section 5.4, The .idata Section. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_PE_COFF_IDATA_PASS_H +#define LLD_READER_WRITER_PE_COFF_IDATA_PASS_H + +#include "Atoms.h" +#include "lld/Core/File.h" +#include "lld/Core/Pass.h" +#include "lld/Core/Simple.h" +#include "lld/ReaderWriter/PECOFFLinkingContext.h" +#include "llvm/Support/COFF.h" +#include <algorithm> +#include <map> + +using llvm::COFF::ImportDirectoryTableEntry; + +namespace lld { +namespace pecoff { +namespace idata { + +class DLLNameAtom; +class HintNameAtom; +class ImportTableEntryAtom; + +// A state object of this pass. +struct IdataContext { + IdataContext(MutableFile &f, VirtualFile &g, const PECOFFLinkingContext &c) + : file(f), dummyFile(g), ctx(c) {} + MutableFile &file; + VirtualFile &dummyFile; + const PECOFFLinkingContext &ctx; +}; + +/// The root class of all idata atoms. +class IdataAtom : public COFFLinkerInternalAtom { +public: + SectionChoice sectionChoice() const override { return sectionCustomRequired; } + StringRef customSectionName() const override { return ".idata"; } + ContentType contentType() const override { return typeData; } + ContentPermissions permissions() const override { return permR__; } + +protected: + IdataAtom(IdataContext &context, std::vector<uint8_t> data); +}; + +/// A HintNameAtom represents a symbol that will be imported from a DLL at +/// runtime. It consists with an optional hint, which is a small integer, and a +/// symbol name. +/// +/// A hint is an index of the export pointer table in a DLL. If the import +/// library and DLL is in sync (i.e., ".lib" and ".dll" is for the same version +/// or the symbol ordinal is maintained by hand with ".exp" file), the PE/COFF +/// loader can find the symbol quickly. +class HintNameAtom : public IdataAtom { +public: + HintNameAtom(IdataContext &context, uint16_t hint, StringRef importName); + + StringRef getContentString() { return _importName; } + +private: + std::vector<uint8_t> assembleRawContent(uint16_t hint, StringRef importName); + StringRef _importName; +}; + +class ImportTableEntryAtom : public IdataAtom { +public: + ImportTableEntryAtom(IdataContext &ctx, uint64_t contents, + StringRef sectionName) + : IdataAtom(ctx, assembleRawContent(contents, ctx.ctx.is64Bit())), + _sectionName(sectionName) {} + + StringRef customSectionName() const override { + return _sectionName; + }; + +private: + std::vector<uint8_t> assembleRawContent(uint64_t contents, bool is64); + StringRef _sectionName; +}; + +/// An ImportDirectoryAtom includes information to load a DLL, including a DLL +/// name, symbols that will be resolved from the DLL, and the import address +/// table that are overwritten by the loader with the pointers to the referenced +/// items. The executable has one ImportDirectoryAtom per one imported DLL. +class ImportDirectoryAtom : public IdataAtom { +public: + ImportDirectoryAtom(IdataContext &context, StringRef loadName, + const std::vector<COFFSharedLibraryAtom *> &sharedAtoms) + : IdataAtom(context, std::vector<uint8_t>(20, 0)) { + addRelocations(context, loadName, sharedAtoms); + } + + StringRef customSectionName() const override { return ".idata.d"; } + +private: + void addRelocations(IdataContext &context, StringRef loadName, + const std::vector<COFFSharedLibraryAtom *> &sharedAtoms); + + mutable llvm::BumpPtrAllocator _alloc; +}; + +/// The last NULL entry in the import directory. +class NullImportDirectoryAtom : public IdataAtom { +public: + explicit NullImportDirectoryAtom(IdataContext &context) + : IdataAtom(context, std::vector<uint8_t>(20, 0)) {} + + StringRef customSectionName() const override { return ".idata.d"; } +}; + +/// The class for the the delay-load import table. +class DelayImportDirectoryAtom : public IdataAtom { +public: + DelayImportDirectoryAtom( + IdataContext &context, StringRef loadName, + const std::vector<COFFSharedLibraryAtom *> &sharedAtoms) + : IdataAtom(context, createContent()) { + addRelocations(context, loadName, sharedAtoms); + } + + StringRef customSectionName() const override { return ".didat.d"; } + +private: + std::vector<uint8_t> createContent(); + void addRelocations(IdataContext &context, StringRef loadName, + const std::vector<COFFSharedLibraryAtom *> &sharedAtoms); + + mutable llvm::BumpPtrAllocator _alloc; +}; + +/// Terminator of the delay-load import table. The content of this atom is all +/// zero. +class DelayNullImportDirectoryAtom : public IdataAtom { +public: + explicit DelayNullImportDirectoryAtom(IdataContext &context) + : IdataAtom(context, createContent()) {} + StringRef customSectionName() const override { return ".didat.d"; } + +private: + std::vector<uint8_t> createContent() const { + return std::vector<uint8_t>( + sizeof(llvm::object::delay_import_directory_table_entry), 0); + } +}; + +class DelayImportAddressAtom : public IdataAtom { +public: + explicit DelayImportAddressAtom(IdataContext &context) + : IdataAtom(context, createContent(context.ctx)), + _align(Alignment(context.ctx.is64Bit() ? 3 : 2)) {} + StringRef customSectionName() const override { return ".data"; } + ContentPermissions permissions() const override { return permRW_; } + Alignment alignment() const override { return _align; } + +private: + std::vector<uint8_t> createContent(const PECOFFLinkingContext &ctx) const { + return std::vector<uint8_t>(ctx.is64Bit() ? 8 : 4, 0); + } + + Alignment _align; +}; + +// DelayLoaderAtom contains a wrapper function for __delayLoadHelper2. +class DelayLoaderAtom : public IdataAtom { +public: + DelayLoaderAtom(IdataContext &context, const Atom *impAtom, + const Atom *descAtom, const Atom *delayLoadHelperAtom); + StringRef customSectionName() const override { return ".text"; } + ContentPermissions permissions() const override { return permR_X; } + Alignment alignment() const override { return Alignment(0); } + +private: + std::vector<uint8_t> createContent(MachineTypes machine) const; +}; + +} // namespace idata + +class IdataPass : public lld::Pass { +public: + IdataPass(const PECOFFLinkingContext &ctx) : _dummyFile(ctx), _ctx(ctx) {} + + void perform(std::unique_ptr<MutableFile> &file) override; + +private: + std::map<StringRef, std::vector<COFFSharedLibraryAtom *>> + groupByLoadName(MutableFile &file); + + void replaceSharedLibraryAtoms(MutableFile &file); + + // A dummy file with which all the atoms created in the pass will be + // associated. Atoms need to be associated to an input file even if it's not + // read from a file, so we use this object. + VirtualFile _dummyFile; + + const PECOFFLinkingContext &_ctx; + llvm::BumpPtrAllocator _alloc; +}; + +} // namespace pecoff +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/PECOFF/InferSubsystemPass.h b/lib/ReaderWriter/PECOFF/InferSubsystemPass.h new file mode 100644 index 0000000000000..cbf863ee47848 --- /dev/null +++ b/lib/ReaderWriter/PECOFF/InferSubsystemPass.h @@ -0,0 +1,66 @@ +//===- lib/ReaderWriter/PECOFF/InferSubsystemPass.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_PE_COFF_INFER_SUBSYSTEM_PASS_H +#define LLD_READER_WRITER_PE_COFF_INFER_SUBSYSTEM_PASS_H + +#include "Atoms.h" +#include "lld/Core/Pass.h" +#include <vector> + +namespace lld { +namespace pecoff { + +// Infers subsystem from entry point function name. +class InferSubsystemPass : public lld::Pass { +public: + InferSubsystemPass(PECOFFLinkingContext &ctx) : _ctx(ctx) {} + + void perform(std::unique_ptr<MutableFile> &file) override { + if (_ctx.getSubsystem() != WindowsSubsystem::IMAGE_SUBSYSTEM_UNKNOWN) + return; + + if (_ctx.isDll()) { + _ctx.setSubsystem(WindowsSubsystem::IMAGE_SUBSYSTEM_WINDOWS_GUI); + return; + } + + // Scan the resolved symbols to infer the subsystem. + const std::string wWinMain = _ctx.decorateSymbol("wWinMainCRTStartup"); + const std::string wWinMainAt = _ctx.decorateSymbol("wWinMainCRTStartup@"); + const std::string winMain = _ctx.decorateSymbol("WinMainCRTStartup"); + const std::string winMainAt = _ctx.decorateSymbol("WinMainCRTStartup@"); + const std::string wmain = _ctx.decorateSymbol("wmainCRTStartup"); + const std::string wmainAt = _ctx.decorateSymbol("wmainCRTStartup@"); + const std::string main = _ctx.decorateSymbol("mainCRTStartup"); + const std::string mainAt = _ctx.decorateSymbol("mainCRTStartup@"); + + for (const DefinedAtom *atom : file->definedAtoms()) { + if (atom->name() == wWinMain || atom->name().startswith(wWinMainAt) || + atom->name() == winMain || atom->name().startswith(winMainAt)) { + _ctx.setSubsystem(WindowsSubsystem::IMAGE_SUBSYSTEM_WINDOWS_GUI); + return; + } + if (atom->name() == wmain || atom->name().startswith(wmainAt) || + atom->name() == main || atom->name().startswith(mainAt)) { + _ctx.setSubsystem(WindowsSubsystem::IMAGE_SUBSYSTEM_WINDOWS_CUI); + return; + } + } + llvm::report_fatal_error("Failed to infer subsystem"); + } + +private: + PECOFFLinkingContext &_ctx; +}; + +} // namespace pecoff +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.cpp b/lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.cpp new file mode 100644 index 0000000000000..a11410784b8c2 --- /dev/null +++ b/lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.cpp @@ -0,0 +1,48 @@ +//===- lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.cpp --------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LinkerGeneratedSymbolFile.h" + +namespace lld { +namespace pecoff { + +// Find decorated symbol, namely /sym@[0-9]+/ or /\?sym@@.+/. +bool findDecoratedSymbol(PECOFFLinkingContext *ctx, + std::string sym, std::string &res) { + const std::set<std::string> &defined = ctx->definedSymbols(); + // Search for /sym@[0-9]+/ + { + std::string s = sym + '@'; + auto it = defined.lower_bound(s); + for (auto e = defined.end(); it != e; ++it) { + if (!StringRef(*it).startswith(s)) + break; + if (it->size() == s.size()) + continue; + StringRef suffix = StringRef(*it).substr(s.size()); + if (suffix.find_first_not_of("0123456789") != StringRef::npos) + continue; + res = *it; + return true; + } + } + // Search for /\?sym@@.+/ + { + std::string s = "?" + ctx->undecorateSymbol(sym).str() + "@@"; + auto it = defined.lower_bound(s); + if (it != defined.end() && StringRef(*it).startswith(s)) { + res = *it; + return true; + } + } + return false; +} + +} // namespace pecoff +} // namespace lld diff --git a/lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.h b/lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.h new file mode 100644 index 0000000000000..b9764d70bb3bf --- /dev/null +++ b/lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.h @@ -0,0 +1,309 @@ +//===- lib/ReaderWriter/PECOFF/LinkerGeneratedSymbolFile.h ----------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Atoms.h" +#include "lld/Core/Simple.h" +#include "lld/ReaderWriter/PECOFFLinkingContext.h" +#include "llvm/Support/Allocator.h" +#include <algorithm> +#include <mutex> + +using llvm::COFF::WindowsSubsystem; + +namespace lld { +namespace pecoff { + +bool findDecoratedSymbol(PECOFFLinkingContext *ctx, + std::string sym, std::string &res); + +namespace impl { + +/// The defined atom for dllexported symbols with __imp_ prefix. +class ImpPointerAtom : public COFFLinkerInternalAtom { +public: + ImpPointerAtom(const File &file, StringRef symbolName, uint64_t ordinal) + : COFFLinkerInternalAtom(file, /*oridnal*/ 0, std::vector<uint8_t>(4), + symbolName), + _ordinal(ordinal) {} + + uint64_t ordinal() const override { return _ordinal; } + Scope scope() const override { return scopeGlobal; } + ContentType contentType() const override { return typeData; } + Alignment alignment() const override { return Alignment(4); } + ContentPermissions permissions() const override { return permR__; } + +private: + uint64_t _ordinal; +}; + +class ImpSymbolFile : public SimpleFile { +public: + ImpSymbolFile(StringRef defsym, StringRef undefsym, uint64_t ordinal, + bool is64) + : SimpleFile(defsym), _undefined(*this, undefsym), + _defined(*this, defsym, ordinal) { + SimpleReference *ref; + if (is64) { + ref = new SimpleReference(Reference::KindNamespace::COFF, + Reference::KindArch::x86_64, + llvm::COFF::IMAGE_REL_AMD64_ADDR32, + 0, &_undefined, 0); + } else { + ref = new SimpleReference(Reference::KindNamespace::COFF, + Reference::KindArch::x86, + llvm::COFF::IMAGE_REL_I386_DIR32, + 0, &_undefined, 0); + } + _defined.addReference(std::unique_ptr<SimpleReference>(ref)); + addAtom(_defined); + addAtom(_undefined); + }; + +private: + SimpleUndefinedAtom _undefined; + ImpPointerAtom _defined; +}; + +// A file to make Resolver to resolve a symbol TO instead of a symbol FROM, +// using fallback mechanism for an undefined symbol. One can virtually rename an +// undefined symbol using this file. +class SymbolRenameFile : public SimpleFile { +public: + SymbolRenameFile(StringRef from, StringRef to) + : SimpleFile("<symbol-rename>"), _fromSym(from), _toSym(to), + _from(*this, _fromSym, &_to), _to(*this, _toSym) { + addAtom(_from); + }; + +private: + std::string _fromSym; + std::string _toSym; + COFFUndefinedAtom _from; + COFFUndefinedAtom _to; +}; + +} // namespace impl + +// A virtual file containing absolute symbol __ImageBase. __ImageBase (or +// ___ImageBase on x86) is a linker-generated symbol whose address is the same +// as the image base address. +class LinkerGeneratedSymbolFile : public SimpleFile { +public: + LinkerGeneratedSymbolFile(const PECOFFLinkingContext &ctx) + : SimpleFile("<linker-internal-file>"), + _imageBaseAtom(*this, ctx.decorateSymbol("__ImageBase"), + Atom::scopeGlobal, ctx.getBaseAddress()) { + addAtom(_imageBaseAtom); + }; + +private: + SimpleAbsoluteAtom _imageBaseAtom; +}; + +// A LocallyImporteSymbolFile is an archive file containing __imp_ +// symbols for local use. +// +// For each defined symbol, linker creates an implicit defined symbol +// by appending "__imp_" prefix to the original name. The content of +// the implicit symbol is a pointer to the original symbol +// content. This feature allows one to compile and link the following +// code without error, although _imp__hello is not defined in the +// code. (the leading "_" in this example is automatically appended, +// assuming it's x86.) +// +// void hello() { printf("Hello\n"); } +// extern void (*_imp__hello)(); +// int main() { +// _imp__hello(); +// return 0; +// } +// +// This odd feature is for the compatibility with MSVC link.exe. +class LocallyImportedSymbolFile : public SimpleArchiveLibraryFile { +public: + LocallyImportedSymbolFile(const PECOFFLinkingContext &ctx) + : SimpleArchiveLibraryFile("__imp_"), _is64(ctx.is64Bit()), + _ordinal(0) {} + + File *find(StringRef sym, bool dataSymbolOnly) override { + std::string prefix = "__imp_"; + if (!sym.startswith(prefix)) + return nullptr; + StringRef undef = sym.substr(prefix.size()); + return new (_alloc) impl::ImpSymbolFile(sym, undef, _ordinal++, _is64); + } + +private: + bool _is64; + uint64_t _ordinal; + llvm::BumpPtrAllocator _alloc; +}; + +// A ExportedSymbolRenameFile is a virtual archive file for dllexported symbols. +// +// One usually has to specify the exact symbol name to resolve it. That's true +// in most cases for PE/COFF, except the one described below. +// +// DLLExported symbols can be specified using a module definition file. In a +// file, one can write an EXPORT directive followed by symbol names. Such +// symbols may not be fully decorated. +// +// If a symbol FOO is specified to be dllexported by a module definition file, +// linker has to search not only for /FOO/ but also for /FOO@[0-9]+/ for stdcall +// and for /\?FOO@@.+/ for C++. This ambiguous matching semantics does not fit +// well with Resolver. +// +// We could probably modify Resolver to resolve ambiguous symbols, but I think +// we don't want to do that because it'd be rarely used, and only this Windows +// specific feature would use it. It's probably not a good idea to make the core +// linker to be able to deal with it. +// +// So, instead of tweaking Resolver, we chose to do some hack here. An +// ExportedSymbolRenameFile maintains a set containing all possibly defined +// symbol names. That set would be a union of (1) all the defined symbols that +// are already parsed and read and (2) all the defined symbols in archive files +// that are not yet be parsed. +// +// If Resolver asks this file to return an atom for a dllexported symbol, find() +// looks up the set, doing ambiguous matching. If there's a symbol with @ +// prefix, it returns an atom to rename the dllexported symbol, hoping that +// Resolver will find the new symbol with atsign from an archive file at the +// next visit. +class ExportedSymbolRenameFile : public SimpleArchiveLibraryFile { +public: + ExportedSymbolRenameFile(const PECOFFLinkingContext &ctx) + : SimpleArchiveLibraryFile("<export>"), + _ctx(const_cast<PECOFFLinkingContext *>(&ctx)) { + for (PECOFFLinkingContext::ExportDesc &desc : _ctx->getDllExports()) + _exportedSyms.insert(desc.name); + } + + File *find(StringRef sym, bool dataSymbolOnly) override { + typedef PECOFFLinkingContext::ExportDesc ExportDesc; + if (_exportedSyms.count(sym) == 0) + return nullptr; + std::string replace; + if (!findDecoratedSymbol(_ctx, sym.str(), replace)) + return nullptr; + + for (ExportDesc &exp : _ctx->getDllExports()) + if (exp.name == sym) + exp.mangledName = replace; + if (_ctx->deadStrip()) + _ctx->addDeadStripRoot(_ctx->allocate(replace)); + return new (_alloc) impl::SymbolRenameFile(sym, replace); + } + +private: + std::set<std::string> _exportedSyms; + llvm::BumpPtrAllocator _alloc; + PECOFFLinkingContext *_ctx; +}; + +// Windows has not only one but many entry point functions. The +// appropriate one is automatically selected based on the subsystem +// setting and the user-supplied entry point function. +// +// http://msdn.microsoft.com/en-us/library/f9t8842e.aspx +class EntryPointFile : public SimpleFile { +public: + EntryPointFile(const PECOFFLinkingContext &ctx) + : SimpleFile("<entry>"), _ctx(const_cast<PECOFFLinkingContext *>(&ctx)), + _firstTime(true) {} + + const atom_collection<UndefinedAtom> &undefined() const override { + return const_cast<EntryPointFile *>(this)->getUndefinedAtoms(); + } + +private: + const atom_collection<UndefinedAtom> &getUndefinedAtoms() { + std::lock_guard<std::mutex> lock(_mutex); + if (!_firstTime) + return _undefinedAtoms; + _firstTime = false; + + if (_ctx->hasEntry()) { + StringRef entrySym = _ctx->allocate(getEntry()); + _undefinedAtoms._atoms.push_back( + new (_alloc) SimpleUndefinedAtom(*this, entrySym)); + _ctx->setHasEntry(true); + _ctx->setEntrySymbolName(entrySym); + if (_ctx->deadStrip()) + _ctx->addDeadStripRoot(entrySym); + } + return _undefinedAtoms; + } + + // Returns the entry point function name. + std::string getEntry() const { + StringRef opt = _ctx->getEntrySymbolName(); + if (!opt.empty()) { + std::string mangled; + if (findDecoratedSymbol(_ctx, opt, mangled)) + return mangled; + return _ctx->decorateSymbol(opt); + } + return _ctx->decorateSymbol(getDefaultEntry()); + } + + std::string getDefaultEntry() const { + const std::string wWinMainCRTStartup = "wWinMainCRTStartup"; + const std::string WinMainCRTStartup = "WinMainCRTStartup"; + const std::string wmainCRTStartup = "wmainCRTStartup"; + const std::string mainCRTStartup = "mainCRTStartup"; + + if (_ctx->isDll()) { + if (_ctx->getMachineType() == llvm::COFF::IMAGE_FILE_MACHINE_I386) + return "_DllMainCRTStartup@12"; + return "_DllMainCRTStartup"; + } + + // Returns true if a given name exists in an input object file. + auto defined = [&](StringRef name) -> bool { + StringRef sym = _ctx->decorateSymbol(name); + if (_ctx->definedSymbols().count(sym)) + return true; + std::string ignore; + return findDecoratedSymbol(_ctx, sym, ignore); + }; + + switch (_ctx->getSubsystem()) { + case WindowsSubsystem::IMAGE_SUBSYSTEM_UNKNOWN: { + if (defined("wWinMain")) + return wWinMainCRTStartup; + if (defined("WinMain")) + return WinMainCRTStartup; + if (defined("wmain")) + return wmainCRTStartup; + if (!defined("main")) + llvm::errs() << "Cannot infer subsystem; assuming /subsystem:console\n"; + return mainCRTStartup; + } + case WindowsSubsystem::IMAGE_SUBSYSTEM_WINDOWS_GUI: + if (defined("WinMain")) + return WinMainCRTStartup; + return wWinMainCRTStartup; + case WindowsSubsystem::IMAGE_SUBSYSTEM_WINDOWS_CUI: + if (defined("wmain")) + return wmainCRTStartup; + return mainCRTStartup; + default: + return mainCRTStartup; + } + } + + PECOFFLinkingContext *_ctx; + atom_collection_vector<UndefinedAtom> _undefinedAtoms; + std::mutex _mutex; + llvm::BumpPtrAllocator _alloc; + bool _firstTime; +}; + +} // end namespace pecoff +} // end namespace lld diff --git a/lib/ReaderWriter/PECOFF/LoadConfigPass.cpp b/lib/ReaderWriter/PECOFF/LoadConfigPass.cpp new file mode 100644 index 0000000000000..be2f5627f4ea7 --- /dev/null +++ b/lib/ReaderWriter/PECOFF/LoadConfigPass.cpp @@ -0,0 +1,75 @@ +//===- lib/ReaderWriter/PECOFF/LoadConfigPass.cpp -------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// A Load Configuration is a data structure for x86 containing an address of the +// SEH handler table. The Data Directory in the file header points to a load +// configuration. Technically that indirection is not needed but exists for +// historical reasons. +// +// If the file being handled has .sxdata section containing SEH handler table, +// this pass will create a Load Configuration atom. +// +//===----------------------------------------------------------------------===// + +#include "Pass.h" +#include "LoadConfigPass.h" +#include "lld/Core/File.h" +#include "lld/Core/Pass.h" +#include "lld/Core/Simple.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Path.h" +#include <climits> +#include <ctime> +#include <utility> + +using llvm::object::coff_load_configuration32; + +namespace lld { +namespace pecoff { +namespace loadcfg { + +LoadConfigAtom::LoadConfigAtom(VirtualFile &file, const DefinedAtom *sxdata, + int count) + : COFFLinkerInternalAtom( + file, file.getNextOrdinal(), + std::vector<uint8_t>(sizeof(coff_load_configuration32))) { + addDir32Reloc( + this, sxdata, llvm::COFF::IMAGE_FILE_MACHINE_I386, + offsetof(llvm::object::coff_load_configuration32, SEHandlerTable)); + auto *data = getContents<llvm::object::coff_load_configuration32>(); + data->SEHandlerCount = count; +} + +} // namespace loadcfg + +void LoadConfigPass::perform(std::unique_ptr<MutableFile> &file) { + if (_ctx.noSEH()) + return; + + // Find the first atom in .sxdata section. + const DefinedAtom *sxdata = nullptr; + int sectionSize = 0; + for (const DefinedAtom *atom : file->defined()) { + if (atom->customSectionName() == ".sxdata") { + if (!sxdata) + sxdata = atom; + sectionSize += sxdata->size(); + } + } + if (!sxdata) + return; + + auto *loadcfg = new (_alloc) + loadcfg::LoadConfigAtom(_file, sxdata, sectionSize / sizeof(uint32_t)); + file->addAtom(*loadcfg); +} + +} // namespace pecoff +} // namespace lld diff --git a/lib/ReaderWriter/PECOFF/LoadConfigPass.h b/lib/ReaderWriter/PECOFF/LoadConfigPass.h new file mode 100644 index 0000000000000..9f4a25f2b10e2 --- /dev/null +++ b/lib/ReaderWriter/PECOFF/LoadConfigPass.h @@ -0,0 +1,63 @@ +//===- lib/ReaderWriter/PECOFF/LoadConfigPass.h ---------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file \brief This linker pass creates an atom for Load Configuration +/// structure. +/// +/// For the details of the Load Configuration structure, see Microsoft PE/COFF +/// Specification section 5.8. The Load Configuration Structure (Image Only). +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_PE_COFF_LOAD_CONFIG_PASS_H +#define LLD_READER_WRITER_PE_COFF_LOAD_CONFIG_PASS_H + +#include "Atoms.h" +#include "lld/Core/File.h" +#include "lld/Core/Pass.h" +#include "lld/Core/Simple.h" +#include "lld/ReaderWriter/PECOFFLinkingContext.h" +#include <map> + +namespace lld { +namespace pecoff { +namespace loadcfg { + +class LoadConfigAtom : public COFFLinkerInternalAtom { +public: + LoadConfigAtom(VirtualFile &file, const DefinedAtom *sxdata, int count); + + SectionChoice sectionChoice() const override { return sectionCustomRequired; } + StringRef customSectionName() const override { return ".loadcfg"; } + ContentType contentType() const override { return typeData; } + ContentPermissions permissions() const override { return permR__; } + + template <typename T> T *getContents() const { + return (T *)const_cast<uint8_t *>(rawContent().data()); + } +}; + +} // namespace loadcfg + +class LoadConfigPass : public lld::Pass { +public: + LoadConfigPass(PECOFFLinkingContext &ctx) : _ctx(ctx), _file(ctx) {} + + void perform(std::unique_ptr<MutableFile> &file) override; + +private: + PECOFFLinkingContext &_ctx; + VirtualFile _file; + mutable llvm::BumpPtrAllocator _alloc; +}; + +} // namespace pecoff +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/PECOFF/Makefile b/lib/ReaderWriter/PECOFF/Makefile new file mode 100644 index 0000000000000..3ad16969bba71 --- /dev/null +++ b/lib/ReaderWriter/PECOFF/Makefile @@ -0,0 +1,14 @@ +##===- lld/lib/ReaderWriter/PECOFF/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 := lldPECOFF +USEDLIBS = lldCore.a + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/PECOFF/OrderPass.h b/lib/ReaderWriter/PECOFF/OrderPass.h new file mode 100644 index 0000000000000..73133ff73638d --- /dev/null +++ b/lib/ReaderWriter/PECOFF/OrderPass.h @@ -0,0 +1,67 @@ +//===- lib/ReaderWriter/PECOFF/OrderPass.h -------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file \brief This pass sorts atoms by section name, so that they will appear +/// in the correct order in the output. +/// +/// In COFF, sections will be merged into one section by the linker if their +/// names are the same after discarding the "$" character and all characters +/// follow it from their names. The characters following the "$" character +/// determines the merge order. Assume there's an object file containing four +/// data sections in the following order. +/// +/// - .data$2 +/// - .data$3 +/// - .data$1 +/// - .data +/// +/// In this case, the resulting binary should have ".data" section with the +/// contents of ".data", ".data$1", ".data$2" and ".data$3" in that order. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_PE_COFF_ORDER_PASS_H +#define LLD_READER_WRITER_PE_COFF_ORDER_PASS_H + +#include "Atoms.h" +#include "lld/Core/Parallel.h" +#include "lld/Core/Pass.h" +#include <algorithm> + +namespace lld { +namespace pecoff { + +static bool compare(const DefinedAtom *lhs, const DefinedAtom *rhs) { + bool lhsCustom = (lhs->sectionChoice() == DefinedAtom::sectionCustomRequired); + bool rhsCustom = (rhs->sectionChoice() == DefinedAtom::sectionCustomRequired); + if (lhsCustom && rhsCustom) { + int cmp = lhs->customSectionName().compare(rhs->customSectionName()); + if (cmp != 0) + return cmp < 0; + return DefinedAtom::compareByPosition(lhs, rhs); + } + if (lhsCustom && !rhsCustom) + return true; + if (!lhsCustom && rhsCustom) + return false; + return DefinedAtom::compareByPosition(lhs, rhs); +} + +class OrderPass : public lld::Pass { +public: + void perform(std::unique_ptr<MutableFile> &file) override { + MutableFile::DefinedAtomRange defined = file->definedAtoms(); + parallel_sort(defined.begin(), defined.end(), compare); + } +}; + +} // namespace pecoff +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/PECOFF/PDBPass.h b/lib/ReaderWriter/PECOFF/PDBPass.h new file mode 100644 index 0000000000000..0efa054db8231 --- /dev/null +++ b/lib/ReaderWriter/PECOFF/PDBPass.h @@ -0,0 +1,43 @@ +//===- lib/ReaderWriter/PECOFF/PDBPass.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_PE_COFF_PDB_PASS_H +#define LLD_READER_WRITER_PE_COFF_PDB_PASS_H + +#include "lld/Core/Pass.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Process.h" + +namespace lld { +namespace pecoff { + +class PDBPass : public lld::Pass { +public: + PDBPass(PECOFFLinkingContext &ctx) : _ctx(ctx) {} + + void perform(std::unique_ptr<MutableFile> &file) override { + if (_ctx.getDebug()) + touch(_ctx.getPDBFilePath()); + } + +private: + void touch(StringRef path) { + int fd; + if (llvm::sys::fs::openFileForWrite(path, fd, llvm::sys::fs::F_Append)) + llvm::report_fatal_error("failed to create a PDB file"); + llvm::sys::Process::SafelyCloseFileDescriptor(fd); + } + + PECOFFLinkingContext &_ctx; +}; + +} // namespace pecoff +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp b/lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp new file mode 100644 index 0000000000000..6a657e33541d2 --- /dev/null +++ b/lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp @@ -0,0 +1,352 @@ +//===- lib/ReaderWriter/PECOFF/PECOFFLinkingContext.cpp -------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Atoms.h" +#include "EdataPass.h" +#include "IdataPass.h" +#include "InferSubsystemPass.h" +#include "LinkerGeneratedSymbolFile.h" +#include "LoadConfigPass.h" +#include "OrderPass.h" +#include "PDBPass.h" +#include "lld/Core/PassManager.h" +#include "lld/Core/Reader.h" +#include "lld/Core/Simple.h" +#include "lld/Core/Writer.h" +#include "lld/ReaderWriter/PECOFFLinkingContext.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Path.h" +#include <bitset> +#include <climits> +#include <set> + +namespace lld { + +bool PECOFFLinkingContext::validateImpl(raw_ostream &diagnostics) { + if (_stackReserve < _stackCommit) { + diagnostics << "Invalid stack size: reserve size must be equal to or " + << "greater than commit size, but got " << _stackCommit + << " and " << _stackReserve << ".\n"; + return false; + } + + if (_heapReserve < _heapCommit) { + diagnostics << "Invalid heap size: reserve size must be equal to or " + << "greater than commit size, but got " << _heapCommit + << " and " << _heapReserve << ".\n"; + return false; + } + + // It's an error if the base address is not multiple of 64K. + if (getBaseAddress() & 0xffff) { + diagnostics << "Base address have to be multiple of 64K, but got " + << getBaseAddress() << "\n"; + return false; + } + + // Specifing /noentry without /dll is an error. + if (!hasEntry() && !isDll()) { + diagnostics << "/noentry must be specified with /dll\n"; + return false; + } + + // Check for duplicate export ordinals. + std::set<int> exports; + for (const PECOFFLinkingContext::ExportDesc &desc : getDllExports()) { + if (desc.ordinal == -1) + continue; + if (exports.count(desc.ordinal) == 1) { + diagnostics << "Duplicate export ordinals: " << desc.ordinal << "\n"; + return false; + } + exports.insert(desc.ordinal); + } + + // Check for /align. + std::bitset<64> alignment(_sectionDefaultAlignment); + if (alignment.count() != 1) { + diagnostics << "Section alignment must be a power of 2, but got " + << _sectionDefaultAlignment << "\n"; + return false; + } + + _writer = createWriterPECOFF(*this); + return true; +} + +const std::set<std::string> &PECOFFLinkingContext::definedSymbols() { + std::lock_guard<std::recursive_mutex> lock(_mutex); + for (std::unique_ptr<Node> &node : getNodes()) { + if (_seen.count(node.get()) > 0) + continue; + FileNode *fnode = dyn_cast<FileNode>(node.get()); + if (!fnode) + continue; + File *file = fnode->getFile(); + if (file->parse()) + continue; + if (auto *archive = dyn_cast<ArchiveLibraryFile>(file)) { + for (const std::string &sym : archive->getDefinedSymbols()) + _definedSyms.insert(sym); + continue; + } + for (const DefinedAtom *atom : file->defined()) + if (!atom->name().empty()) + _definedSyms.insert(atom->name()); + } + return _definedSyms; +} + +std::unique_ptr<File> PECOFFLinkingContext::createEntrySymbolFile() const { + return LinkingContext::createEntrySymbolFile("<command line option /entry>"); +} + +std::unique_ptr<File> PECOFFLinkingContext::createUndefinedSymbolFile() const { + return LinkingContext::createUndefinedSymbolFile( + "<command line option /include>"); +} + +static int getGroupStartPos(std::vector<std::unique_ptr<Node>> &nodes) { + for (int i = 0, e = nodes.size(); i < e; ++i) + if (GroupEnd *group = dyn_cast<GroupEnd>(nodes[i].get())) + return i - group->getSize(); + llvm::report_fatal_error("internal error"); +} + +void PECOFFLinkingContext::addLibraryFile(std::unique_ptr<FileNode> file) { + GroupEnd *currentGroupEnd; + int pos = -1; + std::vector<std::unique_ptr<Node>> &elements = getNodes(); + for (int i = 0, e = elements.size(); i < e; ++i) { + if ((currentGroupEnd = dyn_cast<GroupEnd>(elements[i].get()))) { + pos = i; + break; + } + } + assert(pos >= 0); + elements.insert(elements.begin() + pos, std::move(file)); + elements[pos + 1] = llvm::make_unique<GroupEnd>( + currentGroupEnd->getSize() + 1); +} + +bool PECOFFLinkingContext::createImplicitFiles( + std::vector<std::unique_ptr<File>> &) { + std::vector<std::unique_ptr<Node>> &members = getNodes(); + + // Create a file for the entry point function. + std::unique_ptr<FileNode> entry(new FileNode( + llvm::make_unique<pecoff::EntryPointFile>(*this))); + members.insert(members.begin() + getGroupStartPos(members), std::move(entry)); + + // Create a file for __ImageBase. + std::unique_ptr<FileNode> fileNode(new FileNode( + llvm::make_unique<pecoff::LinkerGeneratedSymbolFile>(*this))); + members.push_back(std::move(fileNode)); + + // Create a file for _imp_ symbols. + std::unique_ptr<FileNode> impFileNode(new FileNode( + llvm::make_unique<pecoff::LocallyImportedSymbolFile>(*this))); + members.push_back(std::move(impFileNode)); + + // Create a file for dllexported symbols. + std::unique_ptr<FileNode> exportNode(new FileNode( + llvm::make_unique<pecoff::ExportedSymbolRenameFile>(*this))); + addLibraryFile(std::move(exportNode)); + + return true; +} + +/// Returns the section name in the resulting executable. +/// +/// Sections in object files are usually output to the executable with the same +/// name, but you can rename by command line option. /merge:from=to makes the +/// linker to combine "from" section contents to "to" section in the +/// executable. We have a mapping for the renaming. This method looks up the +/// table and returns a new section name if renamed. +StringRef +PECOFFLinkingContext::getOutputSectionName(StringRef sectionName) const { + auto it = _renamedSections.find(sectionName); + if (it == _renamedSections.end()) + return sectionName; + return getOutputSectionName(it->second); +} + +/// Adds a mapping to the section renaming table. This method will be used for +/// /merge command line option. +bool PECOFFLinkingContext::addSectionRenaming(raw_ostream &diagnostics, + StringRef from, StringRef to) { + auto it = _renamedSections.find(from); + if (it != _renamedSections.end()) { + if (it->second == to) + // There's already the same mapping. + return true; + diagnostics << "Section \"" << from << "\" is already mapped to \"" + << it->second << ", so it cannot be mapped to \"" << to << "\"."; + return true; + } + + // Add a mapping, and check if there's no cycle in the renaming mapping. The + // cycle detection algorithm we use here is naive, but that's OK because the + // number of mapping is usually less than 10. + _renamedSections[from] = to; + for (auto elem : _renamedSections) { + StringRef sectionName = elem.first; + std::set<StringRef> visited; + visited.insert(sectionName); + for (;;) { + auto pos = _renamedSections.find(sectionName); + if (pos == _renamedSections.end()) + break; + if (visited.count(pos->second)) { + diagnostics << "/merge:" << from << "=" << to << " makes a cycle"; + return false; + } + sectionName = pos->second; + visited.insert(sectionName); + } + } + return true; +} + +/// Try to find the input library file from the search paths and append it to +/// the input file list. Returns true if the library file is found. +StringRef PECOFFLinkingContext::searchLibraryFile(StringRef filename) const { + // Current directory always takes precedence over the search paths. + if (llvm::sys::path::is_absolute(filename) || llvm::sys::fs::exists(filename)) + return filename; + // Iterate over the search paths. + for (StringRef dir : _inputSearchPaths) { + SmallString<128> path = dir; + llvm::sys::path::append(path, filename); + if (llvm::sys::fs::exists(path.str())) + return allocate(path.str()); + } + return filename; +} + +/// Returns the decorated name of the given symbol name. On 32-bit x86, it +/// adds "_" at the beginning of the string. On other architectures, the +/// return value is the same as the argument. +StringRef PECOFFLinkingContext::decorateSymbol(StringRef name) const { + if (_machineType != llvm::COFF::IMAGE_FILE_MACHINE_I386) + return name; + std::string str = "_"; + str.append(name); + return allocate(str); +} + +StringRef PECOFFLinkingContext::undecorateSymbol(StringRef name) const { + if (_machineType != llvm::COFF::IMAGE_FILE_MACHINE_I386) + return name; + if (!name.startswith("_")) + return name; + return name.substr(1); +} + +uint64_t PECOFFLinkingContext::getBaseAddress() const { + if (_baseAddress == invalidBaseAddress) + return is64Bit() ? pe32PlusDefaultBaseAddress : pe32DefaultBaseAddress; + return _baseAddress; +} + +Writer &PECOFFLinkingContext::writer() const { return *_writer; } + +void PECOFFLinkingContext::setSectionSetMask(StringRef sectionName, + uint32_t newFlags) { + _sectionSetMask[sectionName] |= newFlags; + _sectionClearMask[sectionName] &= ~newFlags; + const uint32_t rwx = (llvm::COFF::IMAGE_SCN_MEM_READ | + llvm::COFF::IMAGE_SCN_MEM_WRITE | + llvm::COFF::IMAGE_SCN_MEM_EXECUTE); + if (newFlags & rwx) + _sectionClearMask[sectionName] |= ~_sectionSetMask[sectionName] & rwx; + assert((_sectionSetMask[sectionName] & _sectionClearMask[sectionName]) == 0); +} + +void PECOFFLinkingContext::setSectionClearMask(StringRef sectionName, + uint32_t newFlags) { + _sectionClearMask[sectionName] |= newFlags; + _sectionSetMask[sectionName] &= ~newFlags; + assert((_sectionSetMask[sectionName] & _sectionClearMask[sectionName]) == 0); +} + +uint32_t PECOFFLinkingContext::getSectionAttributes(StringRef sectionName, + uint32_t flags) const { + auto si = _sectionSetMask.find(sectionName); + uint32_t setMask = (si == _sectionSetMask.end()) ? 0 : si->second; + auto ci = _sectionClearMask.find(sectionName); + uint32_t clearMask = (ci == _sectionClearMask.end()) ? 0 : ci->second; + return (flags | setMask) & ~clearMask; +} + +// Returns true if two export descriptors are the same. +static bool sameExportDesc(const PECOFFLinkingContext::ExportDesc &a, + const PECOFFLinkingContext::ExportDesc &b) { + return a.ordinal == b.ordinal && a.ordinal == b.ordinal && + a.noname == b.noname && a.isData == b.isData; +} + +void PECOFFLinkingContext::addDllExport(ExportDesc &desc) { + addInitialUndefinedSymbol(allocate(desc.name)); + + // MSVC link.exe silently drops characters after the first atsign. + // For example, /export:foo@4=bar is equivalent to /export:foo=bar. + // We do the same thing for compatibility. + if (!desc.externalName.empty()) { + StringRef s(desc.externalName); + size_t pos = s.find('@'); + if (pos != s.npos) + desc.externalName = s.substr(0, pos); + } + + // Scan the vector to look for existing entry. It's not very fast, + // but because the number of exported symbol is usually not that + // much, it should be okay. + for (ExportDesc &e : _dllExports) { + if (e.name != desc.name) + continue; + if (!sameExportDesc(e, desc)) + llvm::errs() << "Export symbol '" << desc.name + << "' specified more than once.\n"; + return; + } + _dllExports.push_back(desc); +} + +static std::string replaceExtension(StringRef path, StringRef ext) { + SmallString<128> ss = path; + llvm::sys::path::replace_extension(ss, ext); + return ss.str(); +} + +std::string PECOFFLinkingContext::getOutputImportLibraryPath() const { + if (!_implib.empty()) + return _implib; + return replaceExtension(outputPath(), ".lib"); +} + +std::string PECOFFLinkingContext::getPDBFilePath() const { + assert(_debug); + if (!_pdbFilePath.empty()) + return _pdbFilePath; + return replaceExtension(outputPath(), ".pdb"); +} + +void PECOFFLinkingContext::addPasses(PassManager &pm) { + pm.add(llvm::make_unique<pecoff::PDBPass>(*this)); + pm.add(llvm::make_unique<pecoff::EdataPass>(*this)); + pm.add(llvm::make_unique<pecoff::IdataPass>(*this)); + pm.add(llvm::make_unique<pecoff::OrderPass>()); + pm.add(llvm::make_unique<pecoff::LoadConfigPass>(*this)); + pm.add(llvm::make_unique<pecoff::InferSubsystemPass>(*this)); +} + +} // end namespace lld diff --git a/lib/ReaderWriter/PECOFF/Pass.cpp b/lib/ReaderWriter/PECOFF/Pass.cpp new file mode 100644 index 0000000000000..ed731984e378b --- /dev/null +++ b/lib/ReaderWriter/PECOFF/Pass.cpp @@ -0,0 +1,95 @@ +//===- lib/ReaderWriter/PECOFF/Pass.cpp -----------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Atoms.h" +#include "Pass.h" +#include "lld/Core/File.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/COFF.h" + +namespace lld { +namespace pecoff { + +static void addReloc(COFFBaseDefinedAtom *atom, const Atom *target, + size_t offsetInAtom, Reference::KindArch arch, + Reference::KindValue relType) { + atom->addReference(llvm::make_unique<SimpleReference>( + Reference::KindNamespace::COFF, arch, relType, offsetInAtom, target, 0)); +} + +void addDir64Reloc(COFFBaseDefinedAtom *atom, const Atom *target, + llvm::COFF::MachineTypes machine, size_t offsetInAtom) { + switch (machine) { + case llvm::COFF::IMAGE_FILE_MACHINE_I386: + addReloc(atom, target, offsetInAtom, Reference::KindArch::x86, + llvm::COFF::IMAGE_REL_I386_DIR32); + return; + case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: + addReloc(atom, target, offsetInAtom, Reference::KindArch::x86_64, + llvm::COFF::IMAGE_REL_AMD64_ADDR64); + return; + default: + llvm_unreachable("unsupported machine type"); + } +} + +void addDir32Reloc(COFFBaseDefinedAtom *atom, const Atom *target, + llvm::COFF::MachineTypes machine, size_t offsetInAtom) { + switch (machine) { + case llvm::COFF::IMAGE_FILE_MACHINE_I386: + addReloc(atom, target, offsetInAtom, Reference::KindArch::x86, + llvm::COFF::IMAGE_REL_I386_DIR32); + return; + case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: + addReloc(atom, target, offsetInAtom, Reference::KindArch::x86_64, + llvm::COFF::IMAGE_REL_AMD64_ADDR32); + return; + default: + llvm_unreachable("unsupported machine type"); + } +} + +void addDir32NBReloc(COFFBaseDefinedAtom *atom, const Atom *target, + llvm::COFF::MachineTypes machine, size_t offsetInAtom) { + switch (machine) { + case llvm::COFF::IMAGE_FILE_MACHINE_I386: + addReloc(atom, target, offsetInAtom, Reference::KindArch::x86, + llvm::COFF::IMAGE_REL_I386_DIR32NB); + return; + case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: + addReloc(atom, target, offsetInAtom, Reference::KindArch::x86_64, + llvm::COFF::IMAGE_REL_AMD64_ADDR32NB); + return; + case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: + addReloc(atom, target, offsetInAtom, Reference::KindArch::ARM, + llvm::COFF::IMAGE_REL_ARM_ADDR32NB); + return; + default: + llvm_unreachable("unsupported machine type"); + } +} + +void addRel32Reloc(COFFBaseDefinedAtom *atom, const Atom *target, + llvm::COFF::MachineTypes machine, size_t offsetInAtom) { + switch (machine) { + case llvm::COFF::IMAGE_FILE_MACHINE_I386: + addReloc(atom, target, offsetInAtom, Reference::KindArch::x86, + llvm::COFF::IMAGE_REL_I386_REL32); + return; + case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: + addReloc(atom, target, offsetInAtom, Reference::KindArch::x86_64, + llvm::COFF::IMAGE_REL_AMD64_REL32); + return; + default: + llvm_unreachable("unsupported machine type"); + } +} + +} // end namespace pecoff +} // end namespace lld diff --git a/lib/ReaderWriter/PECOFF/Pass.h b/lib/ReaderWriter/PECOFF/Pass.h new file mode 100644 index 0000000000000..22466f77859e6 --- /dev/null +++ b/lib/ReaderWriter/PECOFF/Pass.h @@ -0,0 +1,34 @@ +//===- lib/ReaderWriter/PECOFF/Pass.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_PE_COFF_PASS_H +#define LLD_READER_WRITER_PE_COFF_PASS_H + +#include "Atoms.h" +#include "llvm/Support/COFF.h" + +namespace lld { +namespace pecoff { + +void addDir64Reloc(COFFBaseDefinedAtom *atom, const Atom *target, + llvm::COFF::MachineTypes machine, size_t offsetInAtom); + +void addDir32Reloc(COFFBaseDefinedAtom *atom, const Atom *target, + llvm::COFF::MachineTypes machine, size_t offsetInAtom); + +void addDir32NBReloc(COFFBaseDefinedAtom *atom, const Atom *target, + llvm::COFF::MachineTypes machine, size_t offsetInAtom); + +void addRel32Reloc(COFFBaseDefinedAtom *atom, const Atom *target, + llvm::COFF::MachineTypes machine, size_t offsetInAtom); + +} // namespace pecoff +} // namespace lld + +#endif diff --git a/lib/ReaderWriter/PECOFF/ReaderCOFF.cpp b/lib/ReaderWriter/PECOFF/ReaderCOFF.cpp new file mode 100644 index 0000000000000..f060bd8dc0bc0 --- /dev/null +++ b/lib/ReaderWriter/PECOFF/ReaderCOFF.cpp @@ -0,0 +1,1140 @@ +//===- lib/ReaderWriter/PECOFF/ReaderCOFF.cpp -----------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Atoms.h" +#include "lld/Core/Alias.h" +#include "lld/Core/File.h" +#include "lld/Core/Reader.h" +#include "lld/Driver/Driver.h" +#include "lld/ReaderWriter/PECOFFLinkingContext.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/Memory.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <map> +#include <mutex> +#include <set> +#include <system_error> +#include <vector> + +#define DEBUG_TYPE "ReaderCOFF" + +using lld::pecoff::COFFBSSAtom; +using lld::pecoff::COFFDefinedAtom; +using lld::pecoff::COFFDefinedFileAtom; +using lld::pecoff::COFFUndefinedAtom; +using llvm::object::coff_aux_section_definition; +using llvm::object::coff_aux_weak_external; +using llvm::object::coff_relocation; +using llvm::object::coff_section; +using llvm::object::coff_symbol; +using llvm::support::ulittle32_t; + +using namespace lld; + +namespace { + +class BumpPtrStringSaver : public llvm::cl::StringSaver { +public: + const char *SaveString(const char *str) override { + size_t len = strlen(str); + std::lock_guard<std::mutex> lock(_allocMutex); + char *copy = _alloc.Allocate<char>(len + 1); + memcpy(copy, str, len + 1); + return copy; + } + +private: + llvm::BumpPtrAllocator _alloc; + std::mutex _allocMutex; +}; + +class FileCOFF : public File { +private: + typedef std::vector<llvm::object::COFFSymbolRef> SymbolVectorT; + typedef std::map<const coff_section *, SymbolVectorT> SectionToSymbolsT; + +public: + FileCOFF(std::unique_ptr<MemoryBuffer> mb, PECOFFLinkingContext &ctx) + : File(mb->getBufferIdentifier(), kindObject), _mb(std::move(mb)), + _compatibleWithSEH(false), _ordinal(1), + _machineType(llvm::COFF::MT_Invalid), _ctx(ctx) {} + + std::error_code doParse() override; + bool isCompatibleWithSEH() const { return _compatibleWithSEH; } + llvm::COFF::MachineTypes getMachineType() { return _machineType; } + + 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; + } + + void beforeLink() override; + + void addUndefinedSymbol(StringRef sym) { + _undefinedAtoms._atoms.push_back(new (_alloc) COFFUndefinedAtom(*this, sym)); + } + + AliasAtom *createAlias(StringRef name, const DefinedAtom *target, int cnt); + void createAlternateNameAtoms(); + std::error_code parseDirectiveSection(StringRef directives); + + mutable llvm::BumpPtrAllocator _alloc; + +private: + std::error_code readSymbolTable(SymbolVectorT &result); + + void createAbsoluteAtoms(const SymbolVectorT &symbols, + std::vector<const AbsoluteAtom *> &result); + + std::error_code + createUndefinedAtoms(const SymbolVectorT &symbols, + std::vector<const UndefinedAtom *> &result); + + std::error_code + createDefinedSymbols(const SymbolVectorT &symbols, + std::vector<const DefinedAtom *> &result); + + std::error_code cacheSectionAttributes(); + std::error_code maybeCreateSXDataAtoms(); + + std::error_code + AtomizeDefinedSymbolsInSection(const coff_section *section, + SymbolVectorT &symbols, + std::vector<COFFDefinedFileAtom *> &atoms); + + std::error_code + AtomizeDefinedSymbols(SectionToSymbolsT &definedSymbols, + std::vector<const DefinedAtom *> &definedAtoms); + + std::error_code findAtomAt(const coff_section *section, + uint32_t targetAddress, + COFFDefinedFileAtom *&result, + uint32_t &offsetInAtom); + + std::error_code getAtomBySymbolIndex(uint32_t index, Atom *&ret); + + std::error_code + addRelocationReference(const coff_relocation *rel, + const coff_section *section); + + std::error_code getSectionContents(StringRef sectionName, + ArrayRef<uint8_t> &result); + std::error_code getReferenceArch(Reference::KindArch &result); + std::error_code addRelocationReferenceToAtoms(); + std::error_code findSection(StringRef name, const coff_section *&result); + StringRef ArrayRefToString(ArrayRef<uint8_t> array); + uint64_t getNextOrdinal(); + + std::unique_ptr<const llvm::object::COFFObjectFile> _obj; + std::unique_ptr<MemoryBuffer> _mb; + atom_collection_vector<DefinedAtom> _definedAtoms; + atom_collection_vector<UndefinedAtom> _undefinedAtoms; + atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms; + atom_collection_vector<AbsoluteAtom> _absoluteAtoms; + + // The target type of the object. + Reference::KindArch _referenceArch; + + // True if the object has "@feat.00" symbol. + bool _compatibleWithSEH; + + // A map from symbol to its name. All symbols should be in this map except + // unnamed ones. + std::map<llvm::object::COFFSymbolRef, StringRef> _symbolName; + + // A map from symbol to its resultant atom. + std::map<llvm::object::COFFSymbolRef, Atom *> _symbolAtom; + + // A map from symbol to its aux symbol. + std::map<llvm::object::COFFSymbolRef, llvm::object::COFFSymbolRef> _auxSymbol; + + // A map from section to its atoms. + std::map<const coff_section *, std::vector<COFFDefinedFileAtom *>> + _sectionAtoms; + + // A set of COMDAT sections. + std::set<const coff_section *> _comdatSections; + + // A map to get whether the section allows its contents to be merged or not. + std::map<const coff_section *, DefinedAtom::Merge> _merge; + + // COMDAT associative sections + std::multimap<const coff_section *, const coff_section *> _association; + + // A sorted map to find an atom from a section and an offset within + // the section. + std::map<const coff_section *, std::multimap<uint32_t, COFFDefinedAtom *>> + _definedAtomLocations; + + uint64_t _ordinal; + llvm::COFF::MachineTypes _machineType; + PECOFFLinkingContext &_ctx; + mutable BumpPtrStringSaver _stringSaver; +}; + +// Converts the COFF symbol attribute to the LLD's atom attribute. +Atom::Scope getScope(llvm::object::COFFSymbolRef symbol) { + switch (symbol.getStorageClass()) { + case llvm::COFF::IMAGE_SYM_CLASS_EXTERNAL: + return Atom::scopeGlobal; + case llvm::COFF::IMAGE_SYM_CLASS_STATIC: + case llvm::COFF::IMAGE_SYM_CLASS_LABEL: + return Atom::scopeTranslationUnit; + } + llvm_unreachable("Unknown scope"); +} + +DefinedAtom::ContentType getContentType(const coff_section *section) { + if (section->Characteristics & llvm::COFF::IMAGE_SCN_CNT_CODE) + return DefinedAtom::typeCode; + if (section->Characteristics & llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA) + return DefinedAtom::typeData; + if (section->Characteristics & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) + return DefinedAtom::typeZeroFill; + return DefinedAtom::typeUnknown; +} + +DefinedAtom::ContentPermissions getPermissions(const coff_section *section) { + if (section->Characteristics & llvm::COFF::IMAGE_SCN_MEM_READ && + section->Characteristics & llvm::COFF::IMAGE_SCN_MEM_WRITE) + return DefinedAtom::permRW_; + if (section->Characteristics & llvm::COFF::IMAGE_SCN_MEM_READ && + section->Characteristics & llvm::COFF::IMAGE_SCN_MEM_EXECUTE) + return DefinedAtom::permR_X; + if (section->Characteristics & llvm::COFF::IMAGE_SCN_MEM_READ) + return DefinedAtom::permR__; + return DefinedAtom::perm___; +} + +/// Returns the alignment of the section. The contents of the section must be +/// aligned by this value in the resulting executable/DLL. +DefinedAtom::Alignment getAlignment(const coff_section *section) { + if (section->Characteristics & llvm::COFF::IMAGE_SCN_TYPE_NO_PAD) + return DefinedAtom::Alignment(0); + + // Bit [20:24] contains section alignment information. We need to decrease + // the value stored by 1 in order to get the real exponent (e.g, ALIGN_1BYTE + // is 0x00100000, but the exponent should be 0) + uint32_t characteristics = (section->Characteristics >> 20) & 0xf; + + // If all bits are off, we treat it as if ALIGN_1BYTE was on. The PE/COFF spec + // does not say anything about this case, but CVTRES.EXE does not set any bit + // in characteristics[20:24], and its output is intended to be copied to .rsrc + // section with no padding, so I think doing this is the right thing. + if (characteristics == 0) + return DefinedAtom::Alignment(0); + + uint32_t powerOf2 = characteristics - 1; + return DefinedAtom::Alignment(powerOf2); +} + +DefinedAtom::Merge getMerge(const coff_aux_section_definition *auxsym) { + switch (auxsym->Selection) { + case llvm::COFF::IMAGE_COMDAT_SELECT_NODUPLICATES: + return DefinedAtom::mergeNo; + case llvm::COFF::IMAGE_COMDAT_SELECT_ANY: + return DefinedAtom::mergeAsWeakAndAddressUsed; + case llvm::COFF::IMAGE_COMDAT_SELECT_EXACT_MATCH: + // TODO: This mapping is wrong. Fix it. + return DefinedAtom::mergeByContent; + case llvm::COFF::IMAGE_COMDAT_SELECT_SAME_SIZE: + return DefinedAtom::mergeSameNameAndSize; + case llvm::COFF::IMAGE_COMDAT_SELECT_LARGEST: + return DefinedAtom::mergeByLargestSection; + case llvm::COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE: + case llvm::COFF::IMAGE_COMDAT_SELECT_NEWEST: + // FIXME: These attributes has more complicated semantics than the regular + // weak symbol. These are mapped to mergeAsWeakAndAddressUsed for now + // because the core linker does not support them yet. We eventually have + // to implement them for full COFF support. + return DefinedAtom::mergeAsWeakAndAddressUsed; + default: + llvm_unreachable("Unknown merge type"); + } +} + +StringRef getMachineName(llvm::COFF::MachineTypes Type) { + switch (Type) { + default: llvm_unreachable("unsupported machine type"); + case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: + return "ARM"; + case llvm::COFF::IMAGE_FILE_MACHINE_I386: + return "X86"; + case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: + return "X64"; + } +} + +std::error_code FileCOFF::doParse() { + auto binaryOrErr = llvm::object::createBinary(_mb->getMemBufferRef()); + if (std::error_code ec = binaryOrErr.getError()) + return ec; + std::unique_ptr<llvm::object::Binary> bin = std::move(binaryOrErr.get()); + + _obj.reset(dyn_cast<const llvm::object::COFFObjectFile>(bin.get())); + if (!_obj) + return make_error_code(llvm::object::object_error::invalid_file_type); + bin.release(); + + _machineType = static_cast<llvm::COFF::MachineTypes>(_obj->getMachine()); + + if (getMachineType() != llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN && + getMachineType() != _ctx.getMachineType()) { + llvm::errs() << "module machine type '" + << getMachineName(getMachineType()) + << "' conflicts with target machine type '" + << getMachineName(_ctx.getMachineType()) << "'\n"; + return NativeReaderError::conflicting_target_machine; + } + + if (std::error_code ec = getReferenceArch(_referenceArch)) + return ec; + + // Read the symbol table and atomize them if possible. Defined atoms + // cannot be atomized in one pass, so they will be not be atomized but + // added to symbolToAtom. + SymbolVectorT symbols; + if (std::error_code ec = readSymbolTable(symbols)) + return ec; + + createAbsoluteAtoms(symbols, _absoluteAtoms._atoms); + if (std::error_code ec = + createUndefinedAtoms(symbols, _undefinedAtoms._atoms)) + return ec; + if (std::error_code ec = createDefinedSymbols(symbols, _definedAtoms._atoms)) + return ec; + if (std::error_code ec = addRelocationReferenceToAtoms()) + return ec; + if (std::error_code ec = maybeCreateSXDataAtoms()) + return ec; + + // Check for /SAFESEH. + if (_ctx.requireSEH() && !isCompatibleWithSEH()) { + llvm::errs() << "/SAFESEH is specified, but " + << _mb->getBufferIdentifier() + << " is not compatible with SEH.\n"; + return llvm::object::object_error::parse_failed; + } + return std::error_code(); +} + +void FileCOFF::beforeLink() { + // Acquire the mutex to mutate _ctx. + std::lock_guard<std::recursive_mutex> lock(_ctx.getMutex()); + std::set<StringRef> undefSyms; + + // Interpret .drectve section if the section has contents. + ArrayRef<uint8_t> directives; + if (getSectionContents(".drectve", directives)) + return; + if (!directives.empty()) { + std::set<StringRef> orig; + for (StringRef sym : _ctx.initialUndefinedSymbols()) + orig.insert(sym); + if (parseDirectiveSection(ArrayRefToString(directives))) + return; + for (StringRef sym : _ctx.initialUndefinedSymbols()) + if (orig.count(sym) == 0) + undefSyms.insert(sym); + } + + // Add /INCLUDE'ed symbols to the file as if they existed in the + // file as undefined symbols. + for (StringRef sym : undefSyms) { + addUndefinedSymbol(sym); + _ctx.addDeadStripRoot(sym); + } + + // One can define alias symbols using /alternatename:<sym>=<sym> option. + // The mapping for /alternatename is in the context object. This helper + // function iterate over defined atoms and create alias atoms if needed. + createAlternateNameAtoms(); + + // In order to emit SEH table, all input files need to be compatible with + // SEH. Disable SEH if the file being read is not compatible. + if (!isCompatibleWithSEH()) + _ctx.setSafeSEH(false); +} + +/// Iterate over the symbol table to retrieve all symbols. +std::error_code +FileCOFF::readSymbolTable(SymbolVectorT &result) { + for (uint32_t i = 0, e = _obj->getNumberOfSymbols(); i != e; ++i) { + // Retrieve the symbol. + ErrorOr<llvm::object::COFFSymbolRef> sym = _obj->getSymbol(i); + StringRef name; + if (std::error_code ec = sym.getError()) + return ec; + if (sym->getSectionNumber() == llvm::COFF::IMAGE_SYM_DEBUG) + goto next; + result.push_back(*sym); + + if (std::error_code ec = _obj->getSymbolName(*sym, name)) + return ec; + + // Existence of the symbol @feat.00 indicates that object file is compatible + // with Safe Exception Handling. + if (name == "@feat.00") { + _compatibleWithSEH = true; + goto next; + } + + // Cache the name. + _symbolName[*sym] = name; + + // Symbol may be followed by auxiliary symbol table records. The aux + // record can be in any format, but the size is always the same as the + // regular symbol. The aux record supplies additional information for the + // standard symbol. We do not interpret the aux record here, but just + // store it to _auxSymbol. + if (sym->getNumberOfAuxSymbols() > 0) { + ErrorOr<llvm::object::COFFSymbolRef> aux = _obj->getSymbol(i + 1); + if (std::error_code ec = aux.getError()) + return ec; + _auxSymbol[*sym] = *aux; + } + next: + i += sym->getNumberOfAuxSymbols(); + } + return std::error_code(); +} + +/// Create atoms for the absolute symbols. +void FileCOFF::createAbsoluteAtoms(const SymbolVectorT &symbols, + std::vector<const AbsoluteAtom *> &result) { + for (llvm::object::COFFSymbolRef sym : symbols) { + if (sym.getSectionNumber() != llvm::COFF::IMAGE_SYM_ABSOLUTE) + continue; + auto *atom = new (_alloc) SimpleAbsoluteAtom(*this, _symbolName[sym], + getScope(sym), sym.getValue()); + result.push_back(atom); + _symbolAtom[sym] = atom; + } +} + +/// Create atoms for the undefined symbols. This code is bit complicated +/// because it supports "weak externals" mechanism of COFF. If an undefined +/// symbol (sym1) has auxiliary data, the data contains a symbol table index +/// at which the "second symbol" (sym2) for sym1 exists. If sym1 is resolved, +/// it's linked normally. If not, sym1 is resolved as if it has sym2's +/// name. This relationship between sym1 and sym2 is represented using +/// fallback mechanism of undefined symbol. +std::error_code +FileCOFF::createUndefinedAtoms(const SymbolVectorT &symbols, + std::vector<const UndefinedAtom *> &result) { + std::map<llvm::object::COFFSymbolRef, llvm::object::COFFSymbolRef> + weakExternal; + std::set<llvm::object::COFFSymbolRef> fallback; + for (llvm::object::COFFSymbolRef sym : symbols) { + if (sym.getSectionNumber() != llvm::COFF::IMAGE_SYM_UNDEFINED) + continue; + // Create a mapping from sym1 to sym2, if the undefined symbol has + // auxiliary data. + auto iter = _auxSymbol.find(sym); + if (iter == _auxSymbol.end()) + continue; + const coff_aux_weak_external *aux = + reinterpret_cast<const coff_aux_weak_external *>( + iter->second.getRawPtr()); + ErrorOr<llvm::object::COFFSymbolRef> sym2 = _obj->getSymbol(aux->TagIndex); + if (std::error_code ec = sym2.getError()) + return ec; + weakExternal[sym] = *sym2; + fallback.insert(*sym2); + } + + // Create atoms for the undefined symbols. + for (llvm::object::COFFSymbolRef sym : symbols) { + if (sym.getSectionNumber() != llvm::COFF::IMAGE_SYM_UNDEFINED) + continue; + if (fallback.count(sym) > 0) + continue; + + // If the symbol has sym2, create an undefiend atom for sym2, so that we + // can pass it as a fallback atom. + UndefinedAtom *fallback = nullptr; + auto iter = weakExternal.find(sym); + if (iter != weakExternal.end()) { + llvm::object::COFFSymbolRef sym2 = iter->second; + fallback = new (_alloc) COFFUndefinedAtom(*this, _symbolName[sym2]); + _symbolAtom[sym2] = fallback; + } + + // Create an atom for the symbol. + auto *atom = + new (_alloc) COFFUndefinedAtom(*this, _symbolName[sym], fallback); + result.push_back(atom); + _symbolAtom[sym] = atom; + } + return std::error_code(); +} + +/// Create atoms for the defined symbols. This pass is a bit complicated than +/// the other two, because in order to create the atom for the defined symbol +/// we need to know the adjacent symbols. +std::error_code +FileCOFF::createDefinedSymbols(const SymbolVectorT &symbols, + std::vector<const DefinedAtom *> &result) { + // A defined atom can be merged if its section attribute allows its contents + // to be merged. In COFF, it's not very easy to get the section attribute + // for the symbol, so scan all sections in advance and cache the attributes + // for later use. + if (std::error_code ec = cacheSectionAttributes()) + return ec; + + // Filter non-defined atoms, and group defined atoms by its section. + SectionToSymbolsT definedSymbols; + for (llvm::object::COFFSymbolRef sym : symbols) { + // A symbol with section number 0 and non-zero value represents a common + // symbol. The MS COFF spec did not give a definition of what the common + // symbol is. We should probably follow ELF's definition shown below. + // + // - If one object file has a common symbol and another has a definition, + // the common symbol is treated as an undefined reference. + // - If there is no definition for a common symbol, the program linker + // acts as though it saw a definition initialized to zero of the + // appropriate size. + // - Two object files may have common symbols of + // different sizes, in which case the program linker will use the + // largest size. + // + // FIXME: We are currently treating the common symbol as a normal + // mergeable atom. Implement the above semantcis. + if (sym.getSectionNumber() == llvm::COFF::IMAGE_SYM_UNDEFINED && + sym.getValue() > 0) { + StringRef name = _symbolName[sym]; + uint32_t size = sym.getValue(); + auto *atom = new (_alloc) + COFFBSSAtom(*this, name, getScope(sym), DefinedAtom::permRW_, + DefinedAtom::mergeAsWeakAndAddressUsed, size, getNextOrdinal()); + + // Common symbols should be aligned on natural boundaries with the maximum + // of 32 byte. It's not documented anywhere, but it's what MSVC link.exe + // seems to be doing. + uint64_t alignment = std::min((uint64_t)32, llvm::NextPowerOf2(size)); + atom->setAlignment( + DefinedAtom::Alignment(llvm::countTrailingZeros(alignment))); + result.push_back(atom); + continue; + } + + // Skip if it's not for defined atom. + if (sym.getSectionNumber() == llvm::COFF::IMAGE_SYM_DEBUG || + sym.getSectionNumber() == llvm::COFF::IMAGE_SYM_ABSOLUTE || + sym.getSectionNumber() == llvm::COFF::IMAGE_SYM_UNDEFINED) + continue; + + const coff_section *sec; + if (std::error_code ec = _obj->getSection(sym.getSectionNumber(), sec)) + return ec; + assert(sec && "SectionIndex > 0, Sec must be non-null!"); + + uint8_t sc = sym.getStorageClass(); + if (sc != llvm::COFF::IMAGE_SYM_CLASS_EXTERNAL && + sc != llvm::COFF::IMAGE_SYM_CLASS_STATIC && + sc != llvm::COFF::IMAGE_SYM_CLASS_FUNCTION && + sc != llvm::COFF::IMAGE_SYM_CLASS_LABEL) { + llvm::errs() << "Unable to create atom for: " << _symbolName[sym] << " (" + << static_cast<int>(sc) << ")\n"; + return llvm::object::object_error::parse_failed; + } + + definedSymbols[sec].push_back(sym); + } + + // Atomize the defined symbols. + if (std::error_code ec = AtomizeDefinedSymbols(definedSymbols, result)) + return ec; + + return std::error_code(); +} + +// Cache the COMDAT attributes, which indicate whether the symbols in the +// section can be merged or not. +std::error_code FileCOFF::cacheSectionAttributes() { + // The COMDAT section attribute is not an attribute of coff_section, but is + // stored in the auxiliary symbol for the first symbol referring a COMDAT + // section. It feels to me that it's unnecessarily complicated, but this is + // how COFF works. + for (auto i : _auxSymbol) { + // Read a section from the file + llvm::object::COFFSymbolRef sym = i.first; + if (sym.getSectionNumber() == llvm::COFF::IMAGE_SYM_ABSOLUTE || + sym.getSectionNumber() == llvm::COFF::IMAGE_SYM_UNDEFINED) + continue; + + const coff_section *sec; + if (std::error_code ec = _obj->getSection(sym.getSectionNumber(), sec)) + return ec; + const coff_aux_section_definition *aux = + reinterpret_cast<const coff_aux_section_definition *>( + i.second.getRawPtr()); + + if (sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_COMDAT) { + // Read aux symbol data. + _comdatSections.insert(sec); + _merge[sec] = getMerge(aux); + } + + // Handle associative sections. + if (aux->Selection == llvm::COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) { + const coff_section *parent; + if (std::error_code ec = + _obj->getSection(aux->getNumber(sym.isBigObj()), parent)) + return ec; + _association.insert(std::make_pair(parent, sec)); + } + } + + // The sections that does not have auxiliary symbol are regular sections, in + // which symbols are not allowed to be merged. + for (const auto §ion : _obj->sections()) { + const coff_section *sec = _obj->getCOFFSection(section); + if (!_merge.count(sec)) + _merge[sec] = DefinedAtom::mergeNo; + } + return std::error_code(); +} + +/// Atomize \p symbols and append the results to \p atoms. The symbols are +/// assumed to have been defined in the \p section. +std::error_code FileCOFF::AtomizeDefinedSymbolsInSection( + const coff_section *section, SymbolVectorT &symbols, + std::vector<COFFDefinedFileAtom *> &atoms) { + // Sort symbols by position. + std::stable_sort( + symbols.begin(), symbols.end(), + [](llvm::object::COFFSymbolRef a, llvm::object::COFFSymbolRef b) + -> bool { return a.getValue() < b.getValue(); }); + + StringRef sectionName; + if (std::error_code ec = _obj->getSectionName(section, sectionName)) + return ec; + + // BSS section does not have contents. If this is the BSS section, create + // COFFBSSAtom instead of COFFDefinedAtom. + if (section->Characteristics & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) { + for (auto si = symbols.begin(), se = symbols.end(); si != se; ++si) { + llvm::object::COFFSymbolRef sym = *si; + uint32_t size = (si + 1 == se) ? section->SizeOfRawData - sym.getValue() + : si[1].getValue() - sym.getValue(); + auto *atom = new (_alloc) COFFBSSAtom( + *this, _symbolName[sym], getScope(sym), getPermissions(section), + DefinedAtom::mergeAsWeakAndAddressUsed, size, getNextOrdinal()); + atoms.push_back(atom); + _symbolAtom[sym] = atom; + } + return std::error_code(); + } + + ArrayRef<uint8_t> secData; + if (std::error_code ec = _obj->getSectionContents(section, secData)) + return ec; + + // A section with IMAGE_SCN_LNK_{INFO,REMOVE} attribute will never become + // a part of the output image. That's what the COFF spec says. + if (section->Characteristics & llvm::COFF::IMAGE_SCN_LNK_INFO || + section->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE) + return std::error_code(); + + // Supporting debug info needs more work than just linking and combining + // .debug sections. We don't support it yet. Let's discard .debug sections at + // the very beginning of the process so that we don't spend time on linking + // blobs that nobody would understand. + if ((section->Characteristics & llvm::COFF::IMAGE_SCN_MEM_DISCARDABLE) && + (sectionName == ".debug" || sectionName.startswith(".debug$"))) { + return std::error_code(); + } + + DefinedAtom::ContentType type = getContentType(section); + DefinedAtom::ContentPermissions perms = getPermissions(section); + uint64_t sectionSize = section->SizeOfRawData; + bool isComdat = (_comdatSections.count(section) == 1); + + // Create an atom for the entire section. + if (symbols.empty()) { + ArrayRef<uint8_t> data(secData.data(), secData.size()); + auto *atom = new (_alloc) COFFDefinedAtom( + *this, "", sectionName, sectionSize, Atom::scopeTranslationUnit, + type, isComdat, perms, _merge[section], data, getNextOrdinal()); + atoms.push_back(atom); + _definedAtomLocations[section].insert(std::make_pair(0, atom)); + return std::error_code(); + } + + // Create an unnamed atom if the first atom isn't at the start of the + // section. + if (symbols[0].getValue() != 0) { + uint64_t size = symbols[0].getValue(); + ArrayRef<uint8_t> data(secData.data(), size); + auto *atom = new (_alloc) COFFDefinedAtom( + *this, "", sectionName, sectionSize, Atom::scopeTranslationUnit, + type, isComdat, perms, _merge[section], data, getNextOrdinal()); + atoms.push_back(atom); + _definedAtomLocations[section].insert(std::make_pair(0, atom)); + } + + for (auto si = symbols.begin(), se = symbols.end(); si != se; ++si) { + const uint8_t *start = secData.data() + si->getValue(); + // if this is the last symbol, take up the remaining data. + const uint8_t *end = (si + 1 == se) ? secData.data() + secData.size() + : secData.data() + (si + 1)->getValue(); + ArrayRef<uint8_t> data(start, end); + auto *atom = new (_alloc) COFFDefinedAtom( + *this, _symbolName[*si], sectionName, sectionSize, getScope(*si), + type, isComdat, perms, _merge[section], data, getNextOrdinal()); + atoms.push_back(atom); + _symbolAtom[*si] = atom; + _definedAtomLocations[section].insert(std::make_pair(si->getValue(), atom)); + } + return std::error_code(); +} + +std::error_code FileCOFF::AtomizeDefinedSymbols( + SectionToSymbolsT &definedSymbols, + std::vector<const DefinedAtom *> &definedAtoms) { + // For each section, make atoms for all the symbols defined in the + // section, and append the atoms to the result objects. + for (auto &i : definedSymbols) { + const coff_section *section = i.first; + SymbolVectorT &symbols = i.second; + std::vector<COFFDefinedFileAtom *> atoms; + if (std::error_code ec = + AtomizeDefinedSymbolsInSection(section, symbols, atoms)) + return ec; + + // Set alignment to the first atom so that the section contents + // will be aligned as specified by the object section header. + if (atoms.size() > 0) + atoms[0]->setAlignment(getAlignment(section)); + + // Connect atoms with layout-after edges. It prevents atoms + // from being GC'ed if there is a reference to one of the atoms + // in the same layout-after chain. In such case we want to emit + // all the atoms appeared in the same chain, because the "live" + // atom may reference other atoms in the same chain. + if (atoms.size() >= 2) + for (auto it = atoms.begin(), e = atoms.end(); it + 1 != e; ++it) + addLayoutEdge(*it, *(it + 1), lld::Reference::kindLayoutAfter); + + for (COFFDefinedFileAtom *atom : atoms) { + _sectionAtoms[section].push_back(atom); + definedAtoms.push_back(atom); + } + } + + // A COMDAT section with SELECT_ASSOCIATIVE attribute refer to other + // section. If the referred section is linked to a binary, the + // referring section needs to be linked too. A typical use case of + // this attribute is a static initializer; a parent is a comdat BSS + // section, and a child is a static initializer code for the data. + // + // We add referring section contents to the referred section's + // associate list, so that Resolver takes care of them. + for (auto i : _association) { + const coff_section *parent = i.first; + const coff_section *child = i.second; + if (_sectionAtoms.count(child)) { + COFFDefinedFileAtom *p = _sectionAtoms[parent][0]; + p->addAssociate(_sectionAtoms[child][0]); + } + } + + return std::error_code(); +} + +/// Find the atom that is at \p targetAddress in \p section. +std::error_code FileCOFF::findAtomAt(const coff_section *section, + uint32_t targetAddress, + COFFDefinedFileAtom *&result, + uint32_t &offsetInAtom) { + auto loc = _definedAtomLocations.find(section); + if (loc == _definedAtomLocations.end()) + return llvm::object::object_error::parse_failed; + std::multimap<uint32_t, COFFDefinedAtom *> &map = loc->second; + + auto it = map.upper_bound(targetAddress); + if (it == map.begin()) + return llvm::object::object_error::parse_failed; + --it; + uint32_t atomAddress = it->first; + result = it->second; + offsetInAtom = targetAddress - atomAddress; + return std::error_code(); +} + +/// Find the atom for the symbol that was at the \p index in the symbol +/// table. +std::error_code FileCOFF::getAtomBySymbolIndex(uint32_t index, Atom *&ret) { + ErrorOr<llvm::object::COFFSymbolRef> symbol = _obj->getSymbol(index); + if (std::error_code ec = symbol.getError()) + return ec; + ret = _symbolAtom[*symbol]; + assert(ret); + return std::error_code(); +} + +/// Add relocation information to an atom based on \p rel. \p rel is an +/// relocation entry for the \p section, and \p atoms are all the atoms +/// defined in the \p section. +std::error_code FileCOFF::addRelocationReference( + const coff_relocation *rel, const coff_section *section) { + // The address of the item which relocation is applied. Section's + // VirtualAddress needs to be added for historical reasons, but the value + // is usually just zero, so adding it is usually no-op. + uint32_t itemAddress = rel->VirtualAddress + section->VirtualAddress; + + Atom *targetAtom = nullptr; + if (std::error_code ec = + getAtomBySymbolIndex(rel->SymbolTableIndex, targetAtom)) + return ec; + + COFFDefinedFileAtom *atom; + uint32_t offsetInAtom; + if (std::error_code ec = findAtomAt(section, itemAddress, atom, offsetInAtom)) + return ec; + atom->addReference(llvm::make_unique<SimpleReference>( + Reference::KindNamespace::COFF, _referenceArch, rel->Type, offsetInAtom, + targetAtom, 0)); + return std::error_code(); +} + +// Read section contents. +std::error_code FileCOFF::getSectionContents(StringRef sectionName, + ArrayRef<uint8_t> &result) { + const coff_section *section = nullptr; + if (std::error_code ec = findSection(sectionName, section)) + return ec; + if (!section) + return std::error_code(); + if (std::error_code ec = _obj->getSectionContents(section, result)) + return ec; + return std::error_code(); +} + +AliasAtom * +FileCOFF::createAlias(StringRef name, const DefinedAtom *target, int cnt) { + AliasAtom *alias = new (_alloc) AliasAtom(*this, name); + alias->addReference(Reference::KindNamespace::all, Reference::KindArch::all, + Reference::kindLayoutAfter, 0, target, 0); + alias->setMerge(DefinedAtom::mergeAsWeak); + if (target->contentType() == DefinedAtom::typeCode) + alias->setDeadStrip(DefinedAtom::deadStripNever); + alias->setOrdinal(target->ordinal() - cnt); + return alias; +} + +void FileCOFF::createAlternateNameAtoms() { + std::vector<AliasAtom *> aliases; + for (const DefinedAtom *atom : defined()) { + int cnt = 1; + for (StringRef alias : _ctx.getAlternateNames(atom->name())) + aliases.push_back(createAlias(alias, atom, cnt++)); + } + for (AliasAtom *alias : aliases) + _definedAtoms._atoms.push_back(alias); +} + +// Interpret the contents of .drectve section. If exists, the section contains +// a string containing command line options. The linker is expected to +// interpret the options as if they were given via the command line. +// +// The section mainly contains /defaultlib (-l in Unix), but can contain any +// options as long as they are valid. +std::error_code +FileCOFF::parseDirectiveSection(StringRef directives) { + DEBUG(llvm::dbgs() << ".drectve: " << directives << "\n"); + + // Split the string into tokens, as the shell would do for argv. + SmallVector<const char *, 16> tokens; + tokens.push_back("link"); // argv[0] is the command name. Will be ignored. + llvm::cl::TokenizeWindowsCommandLine(directives, _stringSaver, tokens); + tokens.push_back(nullptr); + + // Calls the command line parser to interpret the token string as if they + // were given via the command line. + int argc = tokens.size() - 1; + const char **argv = &tokens[0]; + std::string errorMessage; + llvm::raw_string_ostream stream(errorMessage); + PECOFFLinkingContext::ParseDirectives parseDirectives = + _ctx.getParseDirectives(); + bool parseFailed = !parseDirectives(argc, argv, _ctx, stream); + stream.flush(); + // Print error message if error. + if (parseFailed) { + return make_dynamic_error_code( + Twine("Failed to parse '") + directives + "'\n" + + "Reason: " + errorMessage); + } + if (!errorMessage.empty()) { + llvm::errs() << "lld warning: " << errorMessage << "\n"; + } + return std::error_code(); +} + +/// Returns the target machine type of the current object file. +std::error_code FileCOFF::getReferenceArch(Reference::KindArch &result) { + switch (_obj->getMachine()) { + case llvm::COFF::IMAGE_FILE_MACHINE_I386: + result = Reference::KindArch::x86; + return std::error_code(); + case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: + result = Reference::KindArch::x86_64; + return std::error_code(); + case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: + result = Reference::KindArch::ARM; + return std::error_code(); + case llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN: + result = Reference::KindArch::all; + return std::error_code(); + } + llvm::errs() << "Unsupported machine type: 0x" + << llvm::utohexstr(_obj->getMachine()) << '\n'; + return llvm::object::object_error::parse_failed; +} + +/// Add relocation information to atoms. +std::error_code FileCOFF::addRelocationReferenceToAtoms() { + // Relocation entries are defined for each section. + for (const auto &sec : _obj->sections()) { + const coff_section *section = _obj->getCOFFSection(sec); + + // Skip if there's no atom for the section. Currently we do not create any + // atoms for some sections, such as "debug$S", and such sections need to + // be skipped here too. + if (_sectionAtoms.find(section) == _sectionAtoms.end()) + continue; + + for (const auto &reloc : sec.relocations()) { + const coff_relocation *rel = _obj->getCOFFRelocation(reloc); + if (auto ec = addRelocationReference(rel, section)) + return ec; + } + } + return std::error_code(); +} + +// Read .sxdata section if exists. .sxdata is a x86-only section that contains a +// vector of symbol offsets. The symbols pointed by this section are SEH handler +// functions contained in the same object file. The linker needs to construct a +// SEH table and emit it to executable. +// +// On x86, exception handler addresses are in stack, so they are vulnerable to +// stack overflow attack. In order to protect against it, Windows runtime uses +// the SEH table to check if a SEH handler address in stack is a real address of +// a handler created by compiler. +// +// What we want to emit from the linker is a vector of SEH handler VAs, but here +// we have a vector of offsets to the symbol table. So we convert the latter to +// the former. +std::error_code FileCOFF::maybeCreateSXDataAtoms() { + ArrayRef<uint8_t> sxdata; + if (std::error_code ec = getSectionContents(".sxdata", sxdata)) + return ec; + if (sxdata.empty()) + return std::error_code(); + + auto *atom = new (_alloc) COFFDefinedAtom( + *this, "", ".sxdata", 0, Atom::scopeTranslationUnit, + DefinedAtom::typeData, false /*isComdat*/, DefinedAtom::permR__, + DefinedAtom::mergeNo, sxdata, getNextOrdinal()); + + const ulittle32_t *symbolIndex = + reinterpret_cast<const ulittle32_t *>(sxdata.data()); + int numSymbols = sxdata.size() / sizeof(uint32_t); + + for (int i = 0; i < numSymbols; ++i) { + Atom *handlerFunc; + if (std::error_code ec = getAtomBySymbolIndex(symbolIndex[i], handlerFunc)) + return ec; + int offsetInAtom = i * sizeof(uint32_t); + + uint16_t rtype; + switch (_obj->getMachine()) { + case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: + rtype = llvm::COFF::IMAGE_REL_AMD64_ADDR32; + break; + case llvm::COFF::IMAGE_FILE_MACHINE_I386: + rtype = llvm::COFF::IMAGE_REL_I386_DIR32; + break; + default: + llvm_unreachable("unsupported machine type"); + } + + atom->addReference(llvm::make_unique<SimpleReference>( + Reference::KindNamespace::COFF, _referenceArch, rtype, offsetInAtom, + handlerFunc, 0)); + } + + _definedAtoms._atoms.push_back(atom); + return std::error_code(); +} + +/// Find a section by name. +std::error_code FileCOFF::findSection(StringRef name, + const coff_section *&result) { + for (const auto &sec : _obj->sections()) { + const coff_section *section = _obj->getCOFFSection(sec); + StringRef sectionName; + if (auto ec = _obj->getSectionName(section, sectionName)) + return ec; + if (sectionName == name) { + result = section; + return std::error_code(); + } + } + // Section was not found, but it's not an error. This method returns + // an error only when there's a read error. + return std::error_code(); +} + +// Convert ArrayRef<uint8_t> to std::string. The array contains a string which +// may not be terminated by NUL. +StringRef FileCOFF::ArrayRefToString(ArrayRef<uint8_t> array) { + // .drectve sections are encoded in either ASCII or UTF-8 with BOM. + // The PE/COFF spec allows ANSI (Windows-1252 encoding), but seems + // it's no longer in use. + // Skip a UTF-8 byte marker if exists. + if (array.size() >= 3 && array[0] == 0xEF && array[1] == 0xBB && + array[2] == 0xBF) { + array = array.slice(3); + } + if (array.empty()) + return ""; + StringRef s(reinterpret_cast<const char *>(array.data()), array.size()); + s = s.substr(0, s.find_first_of('\0')); + std::string *contents = new (_alloc) std::string(s.data(), s.size()); + return StringRef(*contents).trim(); +} + +// getNextOrdinal returns a monotonically increasaing uint64_t number +// starting from 1. There's a large gap between two numbers returned +// from this function, so that you can put other atoms between them. +uint64_t FileCOFF::getNextOrdinal() { + return _ordinal++ << 32; +} + +class COFFObjectReader : public Reader { +public: + COFFObjectReader(PECOFFLinkingContext &ctx) : _ctx(ctx) {} + + bool canParse(file_magic magic, StringRef ext, + const MemoryBuffer &) const override { + return magic == llvm::sys::fs::file_magic::coff_object; + } + + std::error_code + loadFile(std::unique_ptr<MemoryBuffer> mb, const Registry &, + std::vector<std::unique_ptr<File>> &result) const override { + // Parse the memory buffer as PECOFF file. + auto *file = new FileCOFF(std::move(mb), _ctx); + result.push_back(std::unique_ptr<File>(file)); + return std::error_code(); + } + +private: + PECOFFLinkingContext &_ctx; +}; + +using namespace llvm::COFF; + +const Registry::KindStrings kindStringsI386[] = { + LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_ABSOLUTE), + LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_DIR16), + LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_REL16), + LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_DIR32), + LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_DIR32NB), + LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_SEG12), + LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_SECTION), + LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_SECREL), + LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_TOKEN), + LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_SECREL7), + LLD_KIND_STRING_ENTRY(IMAGE_REL_I386_REL32), + LLD_KIND_STRING_END}; + +const Registry::KindStrings kindStringsAMD64[] = { + LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_ABSOLUTE), + LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_ADDR64), + LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_ADDR32), + LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_ADDR32NB), + LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_REL32), + LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_REL32_1), + LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_REL32_2), + LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_REL32_3), + LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_REL32_4), + LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_REL32_5), + LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_SECTION), + LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_SECREL), + LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_SECREL7), + LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_TOKEN), + LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_SREL32), + LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_PAIR), + LLD_KIND_STRING_ENTRY(IMAGE_REL_AMD64_SSPAN32), + LLD_KIND_STRING_END}; + +const Registry::KindStrings kindStringsARMNT[] = { + LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_ABSOLUTE), + LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_ADDR32), + LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_ADDR32NB), + LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_BRANCH24), + LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_BRANCH11), + LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_TOKEN), + LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_BLX24), + LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_BLX11), + LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_SECTION), + LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_SECREL), + LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_MOV32A), + LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_MOV32T), + LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_BRANCH20T), + LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_BRANCH24T), + LLD_KIND_STRING_ENTRY(IMAGE_REL_ARM_BLX23T), +}; + +} // end namespace anonymous + +namespace lld { + +void Registry::addSupportCOFFObjects(PECOFFLinkingContext &ctx) { + add(std::unique_ptr<Reader>(new COFFObjectReader(ctx))); + addKindTable(Reference::KindNamespace::COFF, Reference::KindArch::x86, + kindStringsI386); + addKindTable(Reference::KindNamespace::COFF, Reference::KindArch::x86_64, + kindStringsAMD64); + addKindTable(Reference::KindNamespace::COFF, Reference::KindArch::ARM, + kindStringsARMNT); +} + +} diff --git a/lib/ReaderWriter/PECOFF/ReaderImportHeader.cpp b/lib/ReaderWriter/PECOFF/ReaderImportHeader.cpp new file mode 100644 index 0000000000000..8c9641376a0d2 --- /dev/null +++ b/lib/ReaderWriter/PECOFF/ReaderImportHeader.cpp @@ -0,0 +1,389 @@ +//===- lib/ReaderWriter/PECOFF/ReaderImportHeader.cpp ---------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file \brief This file provides a way to read an import library member in a +/// .lib file. +/// +/// Archive Files in Windows +/// ======================== +/// +/// In Windows, archive files with .lib file extension serve two different +/// purposes. +/// +/// - For static linking: An archive file in this use case contains multiple +/// regular .obj files and is used for static linking. This is the same +/// usage as .a file in Unix. +/// +/// - For dynamic linking: An archive file in this use case contains pseudo +/// .obj files to describe exported symbols of a DLL. Each pseudo .obj file +/// in an archive has a name of an exported symbol and a DLL filename from +/// which the symbol can be imported. When you link a DLL on Windows, you +/// pass the name of the .lib file for the DLL instead of the DLL filename +/// itself. That is the Windows way of linking against a shared library. +/// +/// This file contains a function to handle the pseudo object file. +/// +/// Windows Loader and Import Address Table +/// ======================================= +/// +/// Windows supports a GOT-like mechanism for DLLs. The executable using DLLs +/// contains a list of DLL names and list of symbols that need to be resolved by +/// the loader. Windows loader maps the executable and all the DLLs to memory, +/// resolves the symbols referencing items in DLLs, and updates the import +/// address table (IAT) in memory. The IAT is an array of pointers to all of the +/// data or functions in DLL referenced by the executable. You cannot access +/// items in DLLs directly. They have to be accessed through an extra level of +/// indirection. +/// +/// So, if you want to access an item in DLL, you have to go through a +/// pointer. How do you actually do that? You need a symbol for a pointer in the +/// IAT. For each symbol defined in a DLL, a symbol with "__imp_" prefix is +/// exported from the DLL for an IAT entry. For example, if you have a global +/// variable "foo" in a DLL, a pointer to the variable is available as +/// "_imp__foo". The IAT is an array of _imp__ symbols. +/// +/// Is this OK? That's not that complicated. Because items in a DLL are not +/// directly accessible, you need to access through a pointer, and the pointer +/// is available as a symbol with _imp__ prefix. +/// +/// Note 1: Although you can write code with _imp__ prefix, today's compiler and +/// linker let you write code as if there's no extra level of indirection. +/// That's why you haven't seen lots of _imp__ in your code. A variable or a +/// function declared with "dllimport" attribute is treated as an item in a DLL, +/// and the compiler automatically mangles its name and inserts the extra level +/// of indirection when accessing the item. Here are some examples: +/// +/// __declspec(dllimport) int var_in_dll; +/// var_in_dll = 3; // is equivalent to *_imp__var_in_dll = 3; +/// +/// __declspec(dllimport) int fn_in_dll(void); +/// fn_in_dll(); // is equivalent to (*_imp__fn_in_dll)(); +/// +/// It's just the compiler rewrites code for you so that you don't need to +/// handle the indirection yourself. +/// +/// Note 2: __declspec(dllimport) is mandatory for data but optional for +/// function. For a function, the linker creates a jump table with the original +/// symbol name, so that the function is accessible without _imp__ prefix. The +/// same function in a DLL can be called through two different symbols if it's +/// not dllimport'ed. +/// +/// (*_imp__fn)() +/// fn() +/// +/// The above functions do the same thing. fn's content is a JMP instruction to +/// branch to the address pointed by _imp__fn. The latter may be a little bit +/// slower than the former because it will execute the extra JMP instruction, +/// but that's usually negligible. +/// +/// If a function is dllimport'ed, which is usually done in a header file, +/// mangled name will be used at compile time so the jump table will not be +/// used. +/// +/// Because there's no way to hide the indirection for data access at link time, +/// data has to be accessed through dllimport'ed symbols or explicit _imp__ +/// prefix. +/// +/// Idata Sections in the Pseudo Object File +/// ======================================== +/// +/// The object file created by cl.exe has several sections whose name starts +/// with ".idata$" followed by a number. The contents of the sections seem the +/// fragments of a complete ".idata" section. These sections has relocations for +/// the data referenced from the idata secton. Generally, the linker discards +/// "$" and all characters that follow from the section name and merges their +/// contents to one section. So, it looks like if everything would work fine, +/// the idata section would naturally be constructed without having any special +/// code for doing that. +/// +/// However, the LLD linker cannot do that. An idata section constructed in that +/// way was never be in valid format. We don't know the reason yet. Our +/// assumption on the idata fragment could simply be wrong, or the LLD linker is +/// not powerful enough to do the job. Meanwhile, we construct the idata section +/// ourselves. All the "idata$" sections in the pseudo object file are currently +/// ignored. +/// +/// Creating Atoms for the Import Address Table +/// =========================================== +/// +/// The function in this file reads a pseudo object file and creates at most two +/// atoms. One is a shared library atom for _imp__ symbol. The another is a +/// defined atom for the JMP instruction if the symbol is for a function. +/// +//===----------------------------------------------------------------------===// + +#include "Atoms.h" +#include "lld/Core/Error.h" +#include "lld/Core/File.h" +#include "lld/Core/SharedLibraryAtom.h" +#include "lld/ReaderWriter/PECOFFLinkingContext.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/COFF.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Memory.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include <cstring> +#include <map> +#include <system_error> +#include <vector> + +using namespace lld; +using namespace lld::pecoff; +using namespace llvm; +using namespace llvm::support::endian; + +#define DEBUG_TYPE "ReaderImportHeader" + +namespace lld { + +namespace { + +// This code is valid both in x86 and x64. +const uint8_t FuncAtomContentX86[] = { + 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // JMP *0x0 + 0xcc, 0xcc // INT 3; INT 3 +}; + +const uint8_t FuncAtomContentARMNT[] = { + 0x40, 0xf2, 0x00, 0x0c, // mov.w ip, #0 + 0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0 + 0xdc, 0xf8, 0x00, 0xf0, // ldr.w pc, [ip] +}; + +static void setJumpInstTarget(COFFLinkerInternalAtom *src, const Atom *dst, + int off, MachineTypes machine) { + SimpleReference *ref; + + switch (machine) { + default: llvm::report_fatal_error("unsupported machine type"); + case llvm::COFF::IMAGE_FILE_MACHINE_I386: + ref = new SimpleReference(Reference::KindNamespace::COFF, + Reference::KindArch::x86, + llvm::COFF::IMAGE_REL_I386_DIR32, + off, dst, 0); + break; + case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: + ref = new SimpleReference(Reference::KindNamespace::COFF, + Reference::KindArch::x86_64, + llvm::COFF::IMAGE_REL_AMD64_REL32, + off, dst, 0); + break; + case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: + ref = new SimpleReference(Reference::KindNamespace::COFF, + Reference::KindArch::ARM, + llvm::COFF::IMAGE_REL_ARM_MOV32T, + off, dst, 0); + break; + } + src->addReference(std::unique_ptr<SimpleReference>(ref)); +} + +/// The defined atom for jump table. +class FuncAtom : public COFFLinkerInternalAtom { +public: + FuncAtom(const File &file, StringRef symbolName, + const COFFSharedLibraryAtom *impAtom, MachineTypes machine) + : COFFLinkerInternalAtom(file, /*oridnal*/ 0, createContent(machine), + symbolName) { + size_t Offset; + + switch (machine) { + default: llvm::report_fatal_error("unsupported machine type"); + case llvm::COFF::IMAGE_FILE_MACHINE_I386: + case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: + Offset = 2; + break; + case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: + Offset = 0; + break; + } + + setJumpInstTarget(this, impAtom, Offset, machine); + } + + uint64_t ordinal() const override { return 0; } + Scope scope() const override { return scopeGlobal; } + ContentType contentType() const override { return typeCode; } + Alignment alignment() const override { return Alignment(1); } + ContentPermissions permissions() const override { return permR_X; } + +private: + std::vector<uint8_t> createContent(MachineTypes machine) const { + const uint8_t *Data; + size_t Size; + + switch (machine) { + default: llvm::report_fatal_error("unsupported machine type"); + case llvm::COFF::IMAGE_FILE_MACHINE_I386: + case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: + Data = FuncAtomContentX86; + Size = sizeof(FuncAtomContentX86); + break; + case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: + Data = FuncAtomContentARMNT; + Size = sizeof(FuncAtomContentARMNT); + break; + } + + return std::vector<uint8_t>(Data, Data + Size); + } +}; + +class FileImportLibrary : public File { +public: + FileImportLibrary(std::unique_ptr<MemoryBuffer> mb, MachineTypes machine) + : File(mb->getBufferIdentifier(), kindSharedLibrary), + _mb(std::move(mb)), _machine(machine) {} + + std::error_code doParse() override { + const char *buf = _mb->getBufferStart(); + const char *end = _mb->getBufferEnd(); + + // The size of the string that follows the header. + uint32_t dataSize + = read32le(buf + offsetof(COFF::ImportHeader, SizeOfData)); + + // Check if the total size is valid. + if (std::size_t(end - buf) != sizeof(COFF::ImportHeader) + dataSize) + return make_error_code(NativeReaderError::unknown_file_format); + + uint16_t hint = read16le(buf + offsetof(COFF::ImportHeader, OrdinalHint)); + StringRef symbolName(buf + sizeof(COFF::ImportHeader)); + StringRef dllName(buf + sizeof(COFF::ImportHeader) + symbolName.size() + 1); + + // TypeInfo is a bitfield. The least significant 2 bits are import + // type, followed by 3 bit import name type. + uint16_t typeInfo = read16le(buf + offsetof(COFF::ImportHeader, TypeInfo)); + int type = typeInfo & 0x3; + int nameType = (typeInfo >> 2) & 0x7; + + // Symbol name used by the linker may be different from the symbol name used + // by the loader. The latter may lack symbol decorations, or may not even + // have name if it's imported by ordinal. + StringRef importName = symbolNameToImportName(symbolName, nameType); + + const COFFSharedLibraryAtom *dataAtom = + addSharedLibraryAtom(hint, symbolName, importName, dllName); + if (type == llvm::COFF::IMPORT_CODE) + addFuncAtom(symbolName, dllName, dataAtom); + + return std::error_code(); + } + + const atom_collection<DefinedAtom> &defined() const override { + return _definedAtoms; + } + + const atom_collection<UndefinedAtom> &undefined() const override { + return _noUndefinedAtoms; + } + + const atom_collection<SharedLibraryAtom> &sharedLibrary() const override { + return _sharedLibraryAtoms; + } + + const atom_collection<AbsoluteAtom> &absolute() const override { + return _noAbsoluteAtoms; + } + +private: + const COFFSharedLibraryAtom *addSharedLibraryAtom(uint16_t hint, + StringRef symbolName, + StringRef importName, + StringRef dllName) { + auto *atom = new (_alloc) + COFFSharedLibraryAtom(*this, hint, symbolName, importName, dllName); + _sharedLibraryAtoms._atoms.push_back(atom); + return atom; + } + + void addFuncAtom(StringRef symbolName, StringRef dllName, + const COFFSharedLibraryAtom *impAtom) { + auto *atom = new (_alloc) FuncAtom(*this, symbolName, impAtom, _machine); + _definedAtoms._atoms.push_back(atom); + } + + atom_collection_vector<DefinedAtom> _definedAtoms; + atom_collection_vector<SharedLibraryAtom> _sharedLibraryAtoms; + mutable llvm::BumpPtrAllocator _alloc; + + // Does the same thing as StringRef::ltrim() but removes at most one + // character. + StringRef ltrim1(StringRef str, const char *chars) const { + if (!str.empty() && strchr(chars, str[0])) + return str.substr(1); + return str; + } + + // Convert the given symbol name to the import symbol name exported by the + // DLL. + StringRef symbolNameToImportName(StringRef symbolName, int nameType) const { + StringRef ret; + switch (nameType) { + case llvm::COFF::IMPORT_ORDINAL: + // The import is by ordinal. No symbol name will be used to identify the + // item in the DLL. Only its ordinal will be used. + return ""; + case llvm::COFF::IMPORT_NAME: + // The import name in this case is identical to the symbol name. + return symbolName; + case llvm::COFF::IMPORT_NAME_NOPREFIX: + // The import name is the symbol name without leading ?, @ or _. + ret = ltrim1(symbolName, "?@_"); + break; + case llvm::COFF::IMPORT_NAME_UNDECORATE: + // Similar to NOPREFIX, but we also need to truncate at the first @. + ret = ltrim1(symbolName, "?@_"); + ret = ret.substr(0, ret.find('@')); + break; + } + std::string *str = new (_alloc) std::string(ret); + return *str; + } + + std::unique_ptr<MemoryBuffer> _mb; + MachineTypes _machine; +}; + +class COFFImportLibraryReader : public Reader { +public: + COFFImportLibraryReader(PECOFFLinkingContext &ctx) : _ctx(ctx) {} + + bool canParse(file_magic magic, StringRef, + const MemoryBuffer &mb) const override { + if (mb.getBufferSize() < sizeof(COFF::ImportHeader)) + return false; + return (magic == llvm::sys::fs::file_magic::coff_import_library); + } + + std::error_code + loadFile(std::unique_ptr<MemoryBuffer> mb, const class Registry &, + std::vector<std::unique_ptr<File> > &result) const override { + auto *file = new FileImportLibrary(std::move(mb), _ctx.getMachineType()); + result.push_back(std::unique_ptr<File>(file)); + return std::error_code(); + } + +private: + PECOFFLinkingContext &_ctx; +}; + +} // end anonymous namespace + +void Registry::addSupportCOFFImportLibraries(PECOFFLinkingContext &ctx) { + add(llvm::make_unique<COFFImportLibraryReader>(ctx)); +} + +} // end namespace lld diff --git a/lib/ReaderWriter/PECOFF/WriterImportLibrary.cpp b/lib/ReaderWriter/PECOFF/WriterImportLibrary.cpp new file mode 100644 index 0000000000000..fd3360f018b6d --- /dev/null +++ b/lib/ReaderWriter/PECOFF/WriterImportLibrary.cpp @@ -0,0 +1,118 @@ +//===- lib/ReaderWriter/PECOFF/WriterImportLibrary.cpp --------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// This file is responsible for creating the Import Library file. +/// +//===----------------------------------------------------------------------===// + +#include "lld/ReaderWriter/PECOFFLinkingContext.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/raw_ostream.h" + +namespace lld { +namespace pecoff { + +/// Creates a .def file containing the list of exported symbols. +static std::string +createModuleDefinitionFile(const PECOFFLinkingContext &ctx) { + std::string ret; + llvm::raw_string_ostream os(ret); + os << "LIBRARY \"" << llvm::sys::path::filename(ctx.outputPath()) << "\"\n" + << "EXPORTS\n"; + + for (const PECOFFLinkingContext::ExportDesc &desc : ctx.getDllExports()) { + // Symbol names in a module-definition file will be mangled by lib.exe, + // so we need to demangle them before writing to a .def file. + os << " "; + if (!desc.externalName.empty()) { + os << desc.externalName; + } else if (!desc.mangledName.empty()) { + os << ctx.undecorateSymbol(desc.mangledName); + } else { + os << ctx.undecorateSymbol(desc.name); + } + + if (!desc.isPrivate) + os << " @" << desc.ordinal; + if (desc.noname) + os << " NONAME"; + if (desc.isData) + os << " DATA"; + if (desc.isPrivate) + os << " PRIVATE"; + os << "\n"; + } + os.flush(); + return ret; +} + +static std::string writeToTempFile(StringRef contents) { + SmallString<128> path; + int fd; + if (llvm::sys::fs::createTemporaryFile("tmp", "def", fd, path)) { + llvm::errs() << "Failed to create temporary file\n"; + return ""; + } + llvm::raw_fd_ostream os(fd, /*shouldClose*/ true); + os << contents; + return path.str(); +} + +static void writeTo(StringRef path, StringRef contents) { + int fd; + if (llvm::sys::fs::openFileForWrite(path, fd, llvm::sys::fs::F_Text)) { + llvm::errs() << "Failed to open " << path << "\n"; + return; + } + llvm::raw_fd_ostream os(fd, /*shouldClose*/ true); + os << contents; +} + +/// Creates a .def file and runs lib.exe on it to create an import library. +void writeImportLibrary(const PECOFFLinkingContext &ctx) { + std::string fileContents = createModuleDefinitionFile(ctx); + + std::string program = "lib.exe"; + ErrorOr<std::string> programPathOrErr = llvm::sys::findProgramByName(program); + if (!programPathOrErr) { + llvm::errs() << "Unable to find " << program << " in PATH\n"; + } else { + const std::string &programPath = *programPathOrErr; + + std::string defPath = writeToTempFile(fileContents); + llvm::FileRemover tmpFile(defPath); + + std::string defArg = "/def:"; + defArg.append(defPath); + std::string outputArg = "/out:"; + outputArg.append(ctx.getOutputImportLibraryPath()); + + std::vector<const char *> args; + args.push_back(programPath.c_str()); + args.push_back("/nologo"); + args.push_back(ctx.is64Bit() ? "/machine:x64" : "/machine:x86"); + args.push_back(defArg.c_str()); + args.push_back(outputArg.c_str()); + args.push_back(nullptr); + + if (llvm::sys::ExecuteAndWait(programPath.c_str(), &args[0]) != 0) + llvm::errs() << program << " failed\n"; + } + + // If /lldmoduledeffile:<filename> is given, make a copy of the + // temporary module definition file. This feature is for unit tests. + if (!ctx.getModuleDefinitionFile().empty()) + writeTo(ctx.getModuleDefinitionFile(), fileContents); +} + +} // end namespace pecoff +} // end namespace lld diff --git a/lib/ReaderWriter/PECOFF/WriterImportLibrary.h b/lib/ReaderWriter/PECOFF/WriterImportLibrary.h new file mode 100644 index 0000000000000..a51b9a3648c5d --- /dev/null +++ b/lib/ReaderWriter/PECOFF/WriterImportLibrary.h @@ -0,0 +1,23 @@ +//===- lib/ReaderWriter/PECOFF/WriterImportLibrary.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_PE_COFF_WRITER_IMPORT_LIBRARY_H +#define LLD_READER_WRITER_PE_COFF_WRITER_IMPORT_LIBRARY_H + +namespace lld { +class PECOFFLinkingContext; + +namespace pecoff { + +void writeImportLibrary(const PECOFFLinkingContext &ctx); + +} // end namespace pecoff +} // end namespace lld + +#endif diff --git a/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp b/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp new file mode 100644 index 0000000000000..d34e2d3d63fde --- /dev/null +++ b/lib/ReaderWriter/PECOFF/WriterPECOFF.cpp @@ -0,0 +1,1417 @@ +//===- lib/ReaderWriter/PECOFF/WriterPECOFF.cpp ---------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// PE/COFF file consists of DOS Header, PE Header, COFF Header and Section +/// Tables followed by raw section data. +/// +/// This writer is responsible for writing Core Linker results to an Windows +/// executable file. +/// +/// This writer currently supports 32 bit PE/COFF for x86 processor only. +/// +//===----------------------------------------------------------------------===// + +#include "Atoms.h" +#include "WriterImportLibrary.h" +#include "lld/Core/DefinedAtom.h" +#include "lld/Core/File.h" +#include "lld/Core/Writer.h" +#include "lld/ReaderWriter/AtomLayout.h" +#include "lld/ReaderWriter/PECOFFLinkingContext.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/COFF.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/Format.h" +#include <algorithm> +#include <cstdlib> +#include <map> +#include <time.h> +#include <vector> + +#define DEBUG_TYPE "WriterPECOFF" + +using namespace llvm::support::endian; + +using llvm::COFF::DataDirectoryIndex; +using llvm::object::coff_runtime_function_x64; +using llvm::support::ulittle16_t; +using llvm::support::ulittle32_t; +using llvm::support::ulittle64_t; + +namespace lld { +namespace pecoff { + +// Disk sector size. Some data needs to be aligned at disk sector boundary in +// file. +static const int SECTOR_SIZE = 512; + +namespace { +class SectionChunk; + +/// A Chunk is an abstract contiguous range in an output file. +class Chunk { +public: + enum Kind { + kindHeader, + kindSection, + kindStringTable, + kindAtomChunk + }; + + explicit Chunk(Kind kind) : _kind(kind), _size(0) {} + virtual ~Chunk() {} + virtual void write(uint8_t *buffer) = 0; + virtual uint64_t size() const { return _size; } + virtual uint64_t onDiskSize() const { return size(); } + virtual uint64_t align() const { return 1; } + + uint64_t fileOffset() const { return _fileOffset; } + void setFileOffset(uint64_t fileOffset) { _fileOffset = fileOffset; } + Kind getKind() const { return _kind; } + +protected: + Kind _kind; + uint64_t _size; + uint64_t _fileOffset; +}; + +/// A HeaderChunk is an abstract class to represent a file header for +/// PE/COFF. The data in the header chunk is metadata about program and will +/// be consumed by the windows loader. HeaderChunks are not mapped to memory +/// when executed. +class HeaderChunk : public Chunk { +public: + HeaderChunk() : Chunk(kindHeader) {} + + static bool classof(const Chunk *c) { return c->getKind() == kindHeader; } +}; + +/// A DOSStubChunk represents the DOS compatible header at the beginning +/// of PE/COFF files. +class DOSStubChunk : public HeaderChunk { +public: + explicit DOSStubChunk(const PECOFFLinkingContext &ctx) + : HeaderChunk(), _context(ctx) { + // Minimum size of DOS stub is 64 bytes. The next block (PE header) needs to + // be aligned on 8 byte boundary. + size_t size = std::max(_context.getDosStub().size(), (size_t)64); + _size = llvm::RoundUpToAlignment(size, 8); + } + + void write(uint8_t *buffer) override { + ArrayRef<uint8_t> array = _context.getDosStub(); + std::memcpy(buffer, array.data(), array.size()); + auto *header = reinterpret_cast<llvm::object::dos_header *>(buffer); + header->AddressOfRelocationTable = sizeof(llvm::object::dos_header); + header->AddressOfNewExeHeader = _size; + } + +private: + const PECOFFLinkingContext &_context; +}; + +/// A PEHeaderChunk represents PE header including COFF header. +template <class PEHeader> +class PEHeaderChunk : public HeaderChunk { +public: + explicit PEHeaderChunk(const PECOFFLinkingContext &ctx); + + void write(uint8_t *buffer) override; + + void setSizeOfHeaders(uint64_t size) { + // Must be multiple of FileAlignment. + _peHeader.SizeOfHeaders = llvm::RoundUpToAlignment(size, SECTOR_SIZE); + } + + void setSizeOfCode(uint64_t size) { _peHeader.SizeOfCode = size; } + void setBaseOfCode(uint32_t rva) { _peHeader.BaseOfCode = rva; } + void setBaseOfData(uint32_t rva); + void setSizeOfImage(uint32_t size) { _peHeader.SizeOfImage = size; } + + void setSizeOfInitializedData(uint64_t size) { + _peHeader.SizeOfInitializedData = size; + } + + void setSizeOfUninitializedData(uint64_t size) { + _peHeader.SizeOfUninitializedData = size; + } + + void setNumberOfSections(uint32_t num) { _coffHeader.NumberOfSections = num; } + void setNumberOfSymbols(uint32_t num) { _coffHeader.NumberOfSymbols = num; } + + void setAddressOfEntryPoint(uint32_t address) { + _peHeader.AddressOfEntryPoint = address; + } + + void setPointerToSymbolTable(uint32_t rva) { + _coffHeader.PointerToSymbolTable = rva; + } + +private: + llvm::object::coff_file_header _coffHeader; + PEHeader _peHeader; +}; + +/// A SectionHeaderTableChunk represents Section Table Header of PE/COFF +/// format, which is a list of section headers. +class SectionHeaderTableChunk : public HeaderChunk { +public: + SectionHeaderTableChunk() : HeaderChunk() {} + void addSection(SectionChunk *chunk); + uint64_t size() const override; + void write(uint8_t *buffer) override; + +private: + static llvm::object::coff_section createSectionHeader(SectionChunk *chunk); + + std::vector<SectionChunk *> _sections; +}; + +class StringTableChunk : public Chunk { +public: + StringTableChunk() : Chunk(kindStringTable) {} + + static bool classof(const Chunk *c) { + return c->getKind() == kindStringTable; + } + + uint32_t addSectionName(StringRef sectionName) { + if (_stringTable.empty()) { + // The string table immediately follows the symbol table. + // We don't really need a symbol table, but some tools (e.g. dumpbin) + // don't like zero-length symbol table. + // Make room for the empty symbol slot, which occupies 18 byte. + // We also need to reserve 4 bytes for the string table header. + int size = sizeof(llvm::object::coff_symbol16) + 4; + _stringTable.insert(_stringTable.begin(), size, 0); + // Set the name of the dummy symbol to the first string table entry. + // It's better than letting dumpbin print out a garabage as a symbol name. + char *off = _stringTable.data() + 4; + write32le(off, 4); + } + uint32_t offset = _stringTable.size(); + _stringTable.insert(_stringTable.end(), sectionName.begin(), + sectionName.end()); + _stringTable.push_back('\0'); + return offset - sizeof(llvm::object::coff_symbol16); + } + + uint64_t size() const override { return _stringTable.size(); } + + void write(uint8_t *buffer) override { + if (_stringTable.empty()) + return; + char *off = _stringTable.data() + sizeof(llvm::object::coff_symbol16); + write32le(off, _stringTable.size()); + std::memcpy(buffer, _stringTable.data(), _stringTable.size()); + } + +private: + std::vector<char> _stringTable; +}; + +class SectionChunk : public Chunk { +public: + uint64_t onDiskSize() const override { + if (_characteristics & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) + return 0; + return llvm::RoundUpToAlignment(size(), SECTOR_SIZE); + } + + uint64_t align() const override { return SECTOR_SIZE; } + uint32_t getCharacteristics() const { return _characteristics; } + StringRef getSectionName() const { return _sectionName; } + virtual uint64_t memAlign() const { return _memAlign; } + + static bool classof(const Chunk *c) { + Kind kind = c->getKind(); + return kind == kindSection || kind == kindAtomChunk; + } + + uint64_t getVirtualAddress() { return _virtualAddress; } + virtual void setVirtualAddress(uint32_t rva) { _virtualAddress = rva; } + + uint32_t getStringTableOffset() const { return _stringTableOffset; } + void setStringTableOffset(uint32_t offset) { _stringTableOffset = offset; } + +protected: + SectionChunk(Kind kind, StringRef sectionName, uint32_t characteristics, + const PECOFFLinkingContext &ctx) + : Chunk(kind), _sectionName(sectionName), + _characteristics(characteristics), _virtualAddress(0), + _stringTableOffset(0), _memAlign(ctx.getPageSize()) {} + +private: + StringRef _sectionName; + const uint32_t _characteristics; + uint64_t _virtualAddress; + uint32_t _stringTableOffset; + uint64_t _memAlign; +}; + +struct BaseReloc { + BaseReloc(uint64_t a, llvm::COFF::BaseRelocationType t) : addr(a), type(t) {} + uint64_t addr; + llvm::COFF::BaseRelocationType type; +}; + +/// An AtomChunk represents a section containing atoms. +class AtomChunk : public SectionChunk { +public: + AtomChunk(const PECOFFLinkingContext &ctx, StringRef name, + const std::vector<const DefinedAtom *> &atoms); + + void write(uint8_t *buffer) override; + + uint64_t memAlign() const override; + void appendAtom(const DefinedAtom *atom); + void buildAtomRvaMap(std::map<const Atom *, uint64_t> &atomRva) const; + + void applyRelocationsARM(uint8_t *buffer, + std::map<const Atom *, uint64_t> &atomRva, + std::vector<uint64_t> §ionRva, + uint64_t imageBaseAddress); + void applyRelocationsX86(uint8_t *buffer, + std::map<const Atom *, uint64_t> &atomRva, + std::vector<uint64_t> §ionRva, + uint64_t imageBaseAddress); + void applyRelocationsX64(uint8_t *buffer, + std::map<const Atom *, uint64_t> &atomRva, + std::vector<uint64_t> §ionRva, + uint64_t imageBaseAddress); + + void printAtomAddresses(uint64_t baseAddr) const; + void addBaseRelocations(std::vector<BaseReloc> &relocSites) const; + + void setVirtualAddress(uint32_t rva) override; + uint64_t getAtomVirtualAddress(StringRef name) const; + + static bool classof(const Chunk *c) { return c->getKind() == kindAtomChunk; } + +protected: + std::vector<AtomLayout *> _atomLayouts; + uint64_t _virtualAddress; + +private: + uint32_t + computeCharacteristics(const PECOFFLinkingContext &ctx, StringRef name, + const std::vector<const DefinedAtom *> &atoms) const { + return ctx.getSectionAttributes(name, + getDefaultCharacteristics(name, atoms)); + } + + uint32_t getDefaultCharacteristics( + StringRef name, const std::vector<const DefinedAtom *> &atoms) const; + + mutable llvm::BumpPtrAllocator _alloc; + llvm::COFF::MachineTypes _machineType; + const PECOFFLinkingContext &_ctx; +}; + +/// A DataDirectoryChunk represents data directory entries that follows the PE +/// header in the output file. An entry consists of an 8 byte field that +/// indicates a relative virtual address (the starting address of the entry data +/// in memory) and 8 byte entry data size. +class DataDirectoryChunk : public HeaderChunk { +public: + DataDirectoryChunk() + : HeaderChunk(), _data(std::vector<llvm::object::data_directory>(16)) {} + + uint64_t size() const override { + return sizeof(llvm::object::data_directory) * _data.size(); + } + + void setField(DataDirectoryIndex index, uint32_t addr, uint32_t size); + void write(uint8_t *buffer) override; + +private: + std::vector<llvm::object::data_directory> _data; +}; + +/// A BaseRelocChunk represents ".reloc" section. +/// +/// .reloc section contains a list of addresses. If the PE/COFF loader decides +/// to load the binary at a memory address different from its preferred base +/// address, which is specified by ImageBase field in the COFF header, the +/// loader needs to relocate the binary, so that all the addresses in the binary +/// point to new locations. The loader will do that by fixing up the addresses +/// specified by .reloc section. +/// +/// The executable is almost always loaded at the preferred base address because +/// it's loaded into an empty address space. The DLL is however an subject of +/// load-time relocation because it may conflict with other DLLs or the +/// executable. +class BaseRelocChunk : public SectionChunk { + typedef std::vector<std::unique_ptr<Chunk> > ChunkVectorT; + +public: + BaseRelocChunk(ChunkVectorT &chunks, const PECOFFLinkingContext &ctx) + : SectionChunk(kindSection, ".reloc", characteristics, ctx), + _ctx(ctx), _contents(createContents(chunks)) {} + + void write(uint8_t *buffer) override { + std::memcpy(buffer, &_contents[0], _contents.size()); + } + + uint64_t size() const override { return _contents.size(); } + +private: + // When loaded into memory, reloc section should be readable and writable. + static const uint32_t characteristics = + llvm::COFF::IMAGE_SCN_MEM_READ | + llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | + llvm::COFF::IMAGE_SCN_MEM_DISCARDABLE; + + std::vector<uint8_t> createContents(ChunkVectorT &chunks) const; + + // Returns a list of RVAs that needs to be relocated if the binary is loaded + // at an address different from its preferred one. + std::vector<BaseReloc> listRelocSites(ChunkVectorT &chunks) const; + + // Create the content of a relocation block. + std::vector<uint8_t> + createBaseRelocBlock(uint64_t pageAddr, const BaseReloc *begin, + const BaseReloc *end) const; + + const PECOFFLinkingContext &_ctx; + std::vector<uint8_t> _contents; +}; + +template <class PEHeader> +PEHeaderChunk<PEHeader>::PEHeaderChunk(const PECOFFLinkingContext &ctx) + : HeaderChunk() { + // Set the size of the chunk and initialize the header with null bytes. + _size = sizeof(llvm::COFF::PEMagic) + sizeof(_coffHeader) + sizeof(_peHeader); + std::memset(&_coffHeader, 0, sizeof(_coffHeader)); + std::memset(&_peHeader, 0, sizeof(_peHeader)); + + _coffHeader.Machine = ctx.getMachineType(); + _coffHeader.TimeDateStamp = time(nullptr); + + // Attributes of the executable. + uint16_t characteristics = llvm::COFF::IMAGE_FILE_EXECUTABLE_IMAGE; + if (!ctx.is64Bit()) + characteristics |= llvm::COFF::IMAGE_FILE_32BIT_MACHINE; + if (ctx.isDll()) + characteristics |= llvm::COFF::IMAGE_FILE_DLL; + if (ctx.getLargeAddressAware() || ctx.is64Bit()) + characteristics |= llvm::COFF::IMAGE_FILE_LARGE_ADDRESS_AWARE; + if (ctx.getSwapRunFromCD()) + characteristics |= llvm::COFF::IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP; + if (ctx.getSwapRunFromNet()) + characteristics |= llvm::COFF::IMAGE_FILE_NET_RUN_FROM_SWAP; + if (!ctx.getBaseRelocationEnabled()) + characteristics |= llvm::COFF::IMAGE_FILE_RELOCS_STRIPPED; + + _coffHeader.Characteristics = characteristics; + + _peHeader.Magic = ctx.is64Bit() ? llvm::COFF::PE32Header::PE32_PLUS + : llvm::COFF::PE32Header::PE32; + + // The address of the executable when loaded into memory. The default for + // DLLs is 0x10000000. The default for executables is 0x400000. + _peHeader.ImageBase = ctx.getBaseAddress(); + + // Sections should be page-aligned when loaded into memory, which is 4KB on + // x86. + _peHeader.SectionAlignment = ctx.getSectionDefaultAlignment(); + + // Sections in an executable file on disk should be sector-aligned (512 byte). + _peHeader.FileAlignment = SECTOR_SIZE; + + // The version number of the resultant executable/DLL. The number is purely + // informative, and neither the linker nor the loader won't use it. User can + // set the value using /version command line option. Default is 0.0. + PECOFFLinkingContext::Version imageVersion = ctx.getImageVersion(); + _peHeader.MajorImageVersion = imageVersion.majorVersion; + _peHeader.MinorImageVersion = imageVersion.minorVersion; + + // The required Windows version number. This is the internal version and + // shouldn't be confused with product name. Windows 7 is version 6.1 and + // Windows 8 is 6.2, for example. + PECOFFLinkingContext::Version minOSVersion = ctx.getMinOSVersion(); + _peHeader.MajorOperatingSystemVersion = minOSVersion.majorVersion; + _peHeader.MinorOperatingSystemVersion = minOSVersion.minorVersion; + _peHeader.MajorSubsystemVersion = minOSVersion.majorVersion; + _peHeader.MinorSubsystemVersion = minOSVersion.minorVersion; + + _peHeader.Subsystem = ctx.getSubsystem(); + + // Despite its name, DLL characteristics field has meaning both for + // executables and DLLs. We are not very sure if the following bits must + // be set, but regular binaries seem to have these bits, so we follow + // them. + uint16_t dllCharacteristics = 0; + if (ctx.noSEH()) + dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_SEH; + if (ctx.isTerminalServerAware()) + dllCharacteristics |= + llvm::COFF::IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE; + if (ctx.isNxCompat()) + dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NX_COMPAT; + if (ctx.getDynamicBaseEnabled()) + dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE; + if (!ctx.getAllowBind()) + dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_BIND; + if (!ctx.getAllowIsolation()) + dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION; + if (ctx.getHighEntropyVA() && ctx.is64Bit()) + dllCharacteristics |= llvm::COFF::IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA; + _peHeader.DLLCharacteristics = dllCharacteristics; + + _peHeader.SizeOfStackReserve = ctx.getStackReserve(); + _peHeader.SizeOfStackCommit = ctx.getStackCommit(); + _peHeader.SizeOfHeapReserve = ctx.getHeapReserve(); + _peHeader.SizeOfHeapCommit = ctx.getHeapCommit(); + + // The number of data directory entries. We always have 16 entries. + _peHeader.NumberOfRvaAndSize = 16; + + // The size of PE header including optional data directory. + _coffHeader.SizeOfOptionalHeader = sizeof(PEHeader) + + _peHeader.NumberOfRvaAndSize * sizeof(llvm::object::data_directory); +} + +template <> +void PEHeaderChunk<llvm::object::pe32_header>::setBaseOfData(uint32_t rva) { + _peHeader.BaseOfData = rva; +} + +template <> +void PEHeaderChunk<llvm::object::pe32plus_header>::setBaseOfData(uint32_t rva) { + // BaseOfData field does not exist in PE32+ header. +} + +template <class PEHeader> +void PEHeaderChunk<PEHeader>::write(uint8_t *buffer) { + std::memcpy(buffer, llvm::COFF::PEMagic, sizeof(llvm::COFF::PEMagic)); + buffer += sizeof(llvm::COFF::PEMagic); + std::memcpy(buffer, &_coffHeader, sizeof(_coffHeader)); + buffer += sizeof(_coffHeader); + std::memcpy(buffer, &_peHeader, sizeof(_peHeader)); +} + +AtomChunk::AtomChunk(const PECOFFLinkingContext &ctx, StringRef sectionName, + const std::vector<const DefinedAtom *> &atoms) + : SectionChunk(kindAtomChunk, sectionName, + computeCharacteristics(ctx, sectionName, atoms), ctx), + _virtualAddress(0), _machineType(ctx.getMachineType()), _ctx(ctx) { + for (auto *a : atoms) + appendAtom(a); +} + +void AtomChunk::write(uint8_t *buffer) { + if (_atomLayouts.empty()) + return; + if (getCharacteristics() & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) + return; + if (getCharacteristics() & llvm::COFF::IMAGE_SCN_CNT_CODE) { + // Fill the section with INT 3 (0xCC) rather than NUL, so that the + // disassembler will not interpret a garbage between atoms as the beginning + // of multi-byte machine code. This does not change the behavior of + // resulting binary but help debugging. + uint8_t *start = buffer + _atomLayouts.front()->_fileOffset; + uint8_t *end = buffer + _atomLayouts.back()->_fileOffset; + memset(start, 0xCC, end - start); + } + + for (const auto *layout : _atomLayouts) { + const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom); + ArrayRef<uint8_t> rawContent = atom->rawContent(); + std::memcpy(buffer + layout->_fileOffset, rawContent.data(), + rawContent.size()); + } +} + +// Add all atoms to the given map. This data will be used to do relocation. +void +AtomChunk::buildAtomRvaMap(std::map<const Atom *, uint64_t> &atomRva) const { + for (const auto *layout : _atomLayouts) + atomRva[layout->_atom] = layout->_virtualAddr; +} + +static int getSectionIndex(uint64_t targetAddr, + const std::vector<uint64_t> §ionRva) { + int i = 1; + for (uint64_t rva : sectionRva) { + if (targetAddr < rva) + return i; + ++i; + } + return i; +} + +static uint32_t getSectionStartAddr(uint64_t targetAddr, + const std::vector<uint64_t> §ionRva) { + // Scan the list of section start addresses to find the section start address + // for the given RVA. + for (int i = 0, e = sectionRva.size(); i < e; ++i) + if (i == e - 1 || (sectionRva[i] <= targetAddr && targetAddr < sectionRva[i + 1])) + return sectionRva[i]; + llvm_unreachable("Section missing"); +} + +static void applyThumbMoveImmediate(ulittle16_t *mov, uint16_t imm) { + // MOVW(T3): |11110|i|10|0|1|0|0|imm4|0|imm3|Rd|imm8| + // imm32 = zext imm4:i:imm3:imm8 + // MOVT(T1): |11110|i|10|1|1|0|0|imm4|0|imm3|Rd|imm8| + // imm16 = imm4:i:imm3:imm8 + mov[0] = + mov[0] | (((imm & 0x0800) >> 11) << 10) | (((imm & 0xf000) >> 12) << 0); + mov[1] = + mov[1] | (((imm & 0x0700) >> 8) << 12) | (((imm & 0x00ff) >> 0) << 0); +} + +static void applyThumbBranchImmediate(ulittle16_t *bl, int32_t imm) { + // BL(T1): |11110|S|imm10|11|J1|1|J2|imm11| + // imm32 = sext S:I1:I2:imm10:imm11:'0' + // B.W(T4): |11110|S|imm10|10|J1|1|J2|imm11| + // imm32 = sext S:I1:I2:imm10:imm11:'0' + // + // I1 = ~(J1 ^ S), I2 = ~(J2 ^ S) + + assert((~abs(imm) & (-1 << 24)) && "bl/b.w out of range"); + + uint32_t S = (imm < 0 ? 1 : 0); + uint32_t J1 = ((~imm & 0x00800000) >> 23) ^ S; + uint32_t J2 = ((~imm & 0x00400000) >> 22) ^ S; + + bl[0] = bl[0] | (((imm & 0x003ff000) >> 12) << 0) | (S << 10); + bl[1] = bl[1] | (((imm & 0x00000ffe) >> 1) << 0) | (J2 << 11) | (J1 << 13); +} + +void AtomChunk::applyRelocationsARM(uint8_t *Buffer, + std::map<const Atom *, uint64_t> &AtomRVA, + std::vector<uint64_t> &SectionRVA, + uint64_t ImageBase) { + Buffer = Buffer + _fileOffset; + parallel_for_each(_atomLayouts.begin(), _atomLayouts.end(), + [&](const AtomLayout *layout) { + const DefinedAtom *Atom = cast<DefinedAtom>(layout->_atom); + for (const Reference *R : *Atom) { + if (R->kindNamespace() != Reference::KindNamespace::COFF) + continue; + + bool AssumeTHUMBCode = false; + if (auto Target = dyn_cast<DefinedAtom>(R->target())) + AssumeTHUMBCode = Target->permissions() == DefinedAtom::permR_X || + Target->permissions() == DefinedAtom::permRWX; + + const auto AtomOffset = R->offsetInAtom(); + const auto FileOffset = layout->_fileOffset; + const auto TargetAddr = AtomRVA[R->target()] | (AssumeTHUMBCode ? 1 : 0); + auto RelocSite16 = + reinterpret_cast<ulittle16_t *>(Buffer + FileOffset + AtomOffset); + auto RelocSite32 = + reinterpret_cast<ulittle32_t *>(Buffer + FileOffset + AtomOffset); + + switch (R->kindValue()) { + default: llvm_unreachable("unsupported relocation type"); + case llvm::COFF::IMAGE_REL_ARM_ADDR32: + *RelocSite32 = *RelocSite32 + TargetAddr + ImageBase; + break; + case llvm::COFF::IMAGE_REL_ARM_ADDR32NB: + *RelocSite32 = *RelocSite32 + TargetAddr; + break; + case llvm::COFF::IMAGE_REL_ARM_MOV32T: + applyThumbMoveImmediate(&RelocSite16[0], (TargetAddr + ImageBase) >> 0); + applyThumbMoveImmediate(&RelocSite16[2], (TargetAddr + ImageBase) >> 16); + break; + case llvm::COFF::IMAGE_REL_ARM_BRANCH24T: + // NOTE: the thumb bit will implicitly be truncated properly + applyThumbBranchImmediate(RelocSite16, + TargetAddr - AtomRVA[Atom] - AtomOffset - 4); + break; + case llvm::COFF::IMAGE_REL_ARM_BLX23T: + // NOTE: the thumb bit will implicitly be truncated properly + applyThumbBranchImmediate(RelocSite16, + TargetAddr - AtomRVA[Atom] - AtomOffset - 4); + break; + } + } + }); +} + +void AtomChunk::applyRelocationsX86(uint8_t *buffer, + std::map<const Atom *, uint64_t> &atomRva, + std::vector<uint64_t> §ionRva, + uint64_t imageBaseAddress) { + buffer += _fileOffset; + parallel_for_each(_atomLayouts.begin(), _atomLayouts.end(), + [&](const AtomLayout *layout) { + const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom); + for (const Reference *ref : *atom) { + // Skip if this reference is not for COFF relocation. + if (ref->kindNamespace() != Reference::KindNamespace::COFF) + continue; + auto relocSite32 = reinterpret_cast<ulittle32_t *>( + buffer + layout->_fileOffset + ref->offsetInAtom()); + auto relocSite16 = reinterpret_cast<ulittle16_t *>(relocSite32); + const Atom *target = ref->target(); + uint64_t targetAddr = atomRva[target]; + // Also account for whatever offset is already stored at the relocation + // site. + switch (ref->kindValue()) { + case llvm::COFF::IMAGE_REL_I386_ABSOLUTE: + // This relocation is no-op. + break; + case llvm::COFF::IMAGE_REL_I386_DIR32: + // Set target's 32-bit VA. + if (auto *abs = dyn_cast<AbsoluteAtom>(target)) + *relocSite32 += abs->value(); + else + *relocSite32 += targetAddr + imageBaseAddress; + break; + case llvm::COFF::IMAGE_REL_I386_DIR32NB: + // Set target's 32-bit RVA. + *relocSite32 += targetAddr; + break; + case llvm::COFF::IMAGE_REL_I386_REL32: { + // Set 32-bit relative address of the target. This relocation is + // usually used for relative branch or call instruction. + uint32_t disp = atomRva[atom] + ref->offsetInAtom() + 4; + *relocSite32 += targetAddr - disp; + break; + } + case llvm::COFF::IMAGE_REL_I386_SECTION: + // The 16-bit section index that contains the target symbol. + *relocSite16 += getSectionIndex(targetAddr, sectionRva); + break; + case llvm::COFF::IMAGE_REL_I386_SECREL: + // The 32-bit relative address from the beginning of the section that + // contains the target symbol. + *relocSite32 += + targetAddr - getSectionStartAddr(targetAddr, sectionRva); + break; + default: + llvm::report_fatal_error("Unsupported relocation kind"); + } + } + }); +} + +void AtomChunk::applyRelocationsX64(uint8_t *buffer, + std::map<const Atom *, uint64_t> &atomRva, + std::vector<uint64_t> §ionRva, + uint64_t imageBase) { + buffer += _fileOffset; + parallel_for_each(_atomLayouts.begin(), _atomLayouts.end(), + [&](const AtomLayout *layout) { + const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom); + for (const Reference *ref : *atom) { + if (ref->kindNamespace() != Reference::KindNamespace::COFF) + continue; + + uint8_t *loc = buffer + layout->_fileOffset + ref->offsetInAtom(); + auto relocSite16 = reinterpret_cast<ulittle16_t *>(loc); + auto relocSite32 = reinterpret_cast<ulittle32_t *>(loc); + auto relocSite64 = reinterpret_cast<ulittle64_t *>(loc); + uint64_t targetAddr = atomRva[ref->target()]; + + switch (ref->kindValue()) { + case llvm::COFF::IMAGE_REL_AMD64_ADDR64: + *relocSite64 += targetAddr + imageBase; + break; + case llvm::COFF::IMAGE_REL_AMD64_ADDR32: + *relocSite32 += targetAddr + imageBase; + break; + case llvm::COFF::IMAGE_REL_AMD64_ADDR32NB: + *relocSite32 += targetAddr; + break; + case llvm::COFF::IMAGE_REL_AMD64_REL32: + *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 4; + break; + case llvm::COFF::IMAGE_REL_AMD64_REL32_1: + *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 5; + break; + case llvm::COFF::IMAGE_REL_AMD64_REL32_2: + *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 6; + break; + case llvm::COFF::IMAGE_REL_AMD64_REL32_3: + *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 7; + break; + case llvm::COFF::IMAGE_REL_AMD64_REL32_4: + *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 8; + break; + case llvm::COFF::IMAGE_REL_AMD64_REL32_5: + *relocSite32 += targetAddr - atomRva[atom] - ref->offsetInAtom() - 9; + break; + case llvm::COFF::IMAGE_REL_AMD64_SECTION: + *relocSite16 += getSectionIndex(targetAddr, sectionRva) - 1; + break; + case llvm::COFF::IMAGE_REL_AMD64_SECREL: + *relocSite32 += + targetAddr - getSectionStartAddr(targetAddr, sectionRva); + break; + default: + llvm::errs() << "Kind: " << (int)ref->kindValue() << "\n"; + llvm::report_fatal_error("Unsupported relocation kind"); + } + } + }); +} + +/// Print atom VAs. Used only for debugging. +void AtomChunk::printAtomAddresses(uint64_t baseAddr) const { + for (const auto *layout : _atomLayouts) { + const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom); + uint64_t addr = layout->_virtualAddr; + llvm::dbgs() << llvm::format("0x%08llx: ", addr + baseAddr) + << (atom->name().empty() ? "(anonymous)" : atom->name()) + << "\n"; + } +} + +/// List all virtual addresses (and not relative virtual addresses) that need +/// to be fixed up if image base is relocated. The only relocation type that +/// needs to be fixed is DIR32 on i386. REL32 is not (and should not be) +/// fixed up because it's PC-relative. +void AtomChunk::addBaseRelocations(std::vector<BaseReloc> &relocSites) const { + for (const auto *layout : _atomLayouts) { + const DefinedAtom *atom = cast<DefinedAtom>(layout->_atom); + for (const Reference *ref : *atom) { + if (ref->kindNamespace() != Reference::KindNamespace::COFF) + continue; + + // An absolute symbol points to a fixed location in memory. Their + // address should not be fixed at load time. One exception is ImageBase + // because that's relative to run-time image base address. + if (auto *abs = dyn_cast<AbsoluteAtom>(ref->target())) + if (!abs->name().equals("__ImageBase") && + !abs->name().equals("___ImageBase")) + continue; + + uint64_t address = layout->_virtualAddr + ref->offsetInAtom(); + switch (_machineType) { + default: llvm_unreachable("unsupported machine type"); + case llvm::COFF::IMAGE_FILE_MACHINE_I386: + if (ref->kindValue() == llvm::COFF::IMAGE_REL_I386_DIR32) + relocSites.push_back( + BaseReloc(address, llvm::COFF::IMAGE_REL_BASED_HIGHLOW)); + break; + case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: + if (ref->kindValue() == llvm::COFF::IMAGE_REL_AMD64_ADDR64) + relocSites.push_back( + BaseReloc(address, llvm::COFF::IMAGE_REL_BASED_DIR64)); + break; + case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: + if (ref->kindValue() == llvm::COFF::IMAGE_REL_ARM_ADDR32) + relocSites.push_back( + BaseReloc(address, llvm::COFF::IMAGE_REL_BASED_HIGHLOW)); + else if (ref->kindValue() == llvm::COFF::IMAGE_REL_ARM_MOV32T) + relocSites.push_back( + BaseReloc(address, llvm::COFF::IMAGE_REL_BASED_ARM_MOV32T)); + break; + } + } + } +} + +void AtomChunk::setVirtualAddress(uint32_t rva) { + SectionChunk::setVirtualAddress(rva); + for (AtomLayout *layout : _atomLayouts) + layout->_virtualAddr += rva; +} + +uint64_t AtomChunk::getAtomVirtualAddress(StringRef name) const { + for (auto atomLayout : _atomLayouts) + if (atomLayout->_atom->name() == name) + return atomLayout->_virtualAddr; + return 0; +} + +void DataDirectoryChunk::setField(DataDirectoryIndex index, uint32_t addr, + uint32_t size) { + llvm::object::data_directory &dir = _data[index]; + dir.RelativeVirtualAddress = addr; + dir.Size = size; +} + +void DataDirectoryChunk::write(uint8_t *buffer) { + std::memcpy(buffer, &_data[0], size()); +} + +uint64_t AtomChunk::memAlign() const { + // ReaderCOFF propagated the section alignment to the first atom in + // the section. We restore that here. + if (_atomLayouts.empty()) + return _ctx.getPageSize(); + int align = _ctx.getPageSize(); + for (auto atomLayout : _atomLayouts) { + auto *atom = cast<const DefinedAtom>(atomLayout->_atom); + align = std::max(align, 1 << atom->alignment().powerOf2); + } + return align; +} + +void AtomChunk::appendAtom(const DefinedAtom *atom) { + // Atom may have to be at a proper alignment boundary. If so, move the + // pointer to make a room after the last atom before adding new one. + _size = llvm::RoundUpToAlignment(_size, 1 << atom->alignment().powerOf2); + + // Create an AtomLayout and move the current pointer. + auto *layout = new (_alloc) AtomLayout(atom, _size, _size); + _atomLayouts.push_back(layout); + _size += atom->size(); +} + +uint32_t AtomChunk::getDefaultCharacteristics( + StringRef name, const std::vector<const DefinedAtom *> &atoms) const { + const uint32_t code = llvm::COFF::IMAGE_SCN_CNT_CODE; + const uint32_t execute = llvm::COFF::IMAGE_SCN_MEM_EXECUTE; + const uint32_t read = llvm::COFF::IMAGE_SCN_MEM_READ; + const uint32_t write = llvm::COFF::IMAGE_SCN_MEM_WRITE; + const uint32_t data = llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; + const uint32_t bss = llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA; + if (name == ".text") + return code | execute | read; + if (name == ".data") + return data | read | write; + if (name == ".rdata") + return data | read; + if (name == ".bss") + return bss | read | write; + assert(atoms.size() > 0); + switch (atoms[0]->permissions()) { + case DefinedAtom::permR__: + return data | read; + case DefinedAtom::permRW_: + return data | read | write; + case DefinedAtom::permR_X: + return code | execute | read; + case DefinedAtom::permRWX: + return code | execute | read | write; + default: + llvm_unreachable("Unsupported permission"); + } +} + +void SectionHeaderTableChunk::addSection(SectionChunk *chunk) { + _sections.push_back(chunk); +} + +uint64_t SectionHeaderTableChunk::size() const { + return _sections.size() * sizeof(llvm::object::coff_section); +} + +void SectionHeaderTableChunk::write(uint8_t *buffer) { + uint64_t offset = 0; + for (SectionChunk *chunk : _sections) { + llvm::object::coff_section header = createSectionHeader(chunk); + std::memcpy(buffer + offset, &header, sizeof(header)); + offset += sizeof(header); + } +} + +llvm::object::coff_section +SectionHeaderTableChunk::createSectionHeader(SectionChunk *chunk) { + llvm::object::coff_section header; + + // We have extended the COFF specification by allowing section names to be + // greater than eight characters. We achieve this by adding the section names + // to the string table. Binutils' linker, ld, performs the same trick. + StringRef sectionName = chunk->getSectionName(); + std::memset(header.Name, 0, llvm::COFF::NameSize); + if (uint32_t stringTableOffset = chunk->getStringTableOffset()) + sprintf(header.Name, "/%u", stringTableOffset); + else + std::strncpy(header.Name, sectionName.data(), sectionName.size()); + + uint32_t characteristics = chunk->getCharacteristics(); + header.VirtualSize = chunk->size(); + header.VirtualAddress = chunk->getVirtualAddress(); + header.SizeOfRawData = chunk->onDiskSize(); + header.PointerToRelocations = 0; + header.PointerToLinenumbers = 0; + header.NumberOfRelocations = 0; + header.NumberOfLinenumbers = 0; + header.Characteristics = characteristics; + + if (characteristics & llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) { + header.PointerToRawData = 0; + } else { + header.PointerToRawData = chunk->fileOffset(); + } + return header; +} + +/// Creates .reloc section content from the other sections. The content of +/// .reloc is basically a list of relocation sites. The relocation sites are +/// divided into blocks. Each block represents the base relocation for a 4K +/// page. +/// +/// By dividing 32 bit RVAs into blocks, COFF saves disk and memory space for +/// the base relocation. A block consists of a 32 bit page RVA and 16 bit +/// relocation entries which represent offsets in the page. That is a more +/// compact representation than a simple vector of 32 bit RVAs. +std::vector<uint8_t> +BaseRelocChunk::createContents(ChunkVectorT &chunks) const { + std::vector<uint8_t> contents; + std::vector<BaseReloc> relocSites = listRelocSites(chunks); + + uint64_t mask = _ctx.getPageSize() - 1; + parallel_sort(relocSites.begin(), relocSites.end(), + [=](const BaseReloc &a, const BaseReloc &b) { + return (a.addr & ~mask) < (b.addr & ~mask); + }); + + // Base relocations for the same memory page are grouped together + // and passed to createBaseRelocBlock. + for (auto it = relocSites.begin(), e = relocSites.end(); it != e;) { + auto beginIt = it; + uint64_t pageAddr = (beginIt->addr & ~mask); + for (++it; it != e; ++it) + if ((it->addr & ~mask) != pageAddr) + break; + const BaseReloc *begin = &*beginIt; + const BaseReloc *end = begin + (it - beginIt); + std::vector<uint8_t> block = createBaseRelocBlock(pageAddr, begin, end); + contents.insert(contents.end(), block.begin(), block.end()); + } + return contents; +} + +// Returns a list of RVAs that needs to be relocated if the binary is loaded +// at an address different from its preferred one. +std::vector<BaseReloc> +BaseRelocChunk::listRelocSites(ChunkVectorT &chunks) const { + std::vector<BaseReloc> ret; + for (auto &cp : chunks) + if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp)) + chunk->addBaseRelocations(ret); + return ret; +} + +// Create the content of a relocation block. +std::vector<uint8_t> +BaseRelocChunk::createBaseRelocBlock(uint64_t pageAddr, + const BaseReloc *begin, + const BaseReloc *end) const { + // Relocation blocks should be padded with IMAGE_REL_I386_ABSOLUTE to be + // aligned to a DWORD size boundary. + uint32_t size = llvm::RoundUpToAlignment( + sizeof(ulittle32_t) * 2 + sizeof(ulittle16_t) * (end - begin), + sizeof(ulittle32_t)); + std::vector<uint8_t> contents(size); + uint8_t *ptr = &contents[0]; + + // The first four bytes is the page RVA. + write32le(ptr, pageAddr); + ptr += sizeof(ulittle32_t); + + // The second four bytes is the size of the block, including the the page + // RVA and this size field. + write32le(ptr, size); + ptr += sizeof(ulittle32_t); + + uint64_t mask = _ctx.getPageSize() - 1; + for (const BaseReloc *i = begin; i < end; ++i) { + write16le(ptr, (i->type << 12) | (i->addr & mask)); + ptr += sizeof(ulittle16_t); + } + return contents; +} + +} // end anonymous namespace + +class PECOFFWriter : public Writer { +public: + explicit PECOFFWriter(const PECOFFLinkingContext &context) + : _ctx(context), _numSections(0), _imageSizeInMemory(_ctx.getPageSize()), + _imageSizeOnDisk(0) {} + + template <class PEHeader> void build(const File &linkedFile); + std::error_code writeFile(const File &linkedFile, StringRef path) override; + +private: + void applyAllRelocations(uint8_t *bufferStart); + void printAllAtomAddresses() const; + void reorderSEHTableEntries(uint8_t *bufferStart); + void reorderSEHTableEntriesX86(uint8_t *bufferStart); + void reorderSEHTableEntriesX64(uint8_t *bufferStart); + + void addChunk(Chunk *chunk); + void addSectionChunk(std::unique_ptr<SectionChunk> chunk, + SectionHeaderTableChunk *table, + StringTableChunk *stringTable); + void setImageSizeOnDisk(); + uint64_t + calcSectionSize(llvm::COFF::SectionCharacteristics sectionType) const; + + uint64_t calcSizeOfInitializedData() const { + return calcSectionSize(llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA); + } + + uint64_t calcSizeOfUninitializedData() const { + return calcSectionSize(llvm::COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA); + } + + uint64_t calcSizeOfCode() const { + return calcSectionSize(llvm::COFF::IMAGE_SCN_CNT_CODE); + } + + std::vector<std::unique_ptr<Chunk> > _chunks; + const PECOFFLinkingContext &_ctx; + uint32_t _numSections; + + // The size of the image in memory. This is initialized with + // _ctx.getPageSize(), as the first page starting at ImageBase is usually left + // unmapped. IIUC there's no technical reason to do so, but we'll follow that + // convention so that we don't produce odd-looking binary. + uint32_t _imageSizeInMemory; + + // The size of the image on disk. This is basically the sum of all chunks in + // the output file with paddings between them. + uint32_t _imageSizeOnDisk; + + // The map from atom to its relative virtual address. + std::map<const Atom *, uint64_t> _atomRva; +}; + +StringRef customSectionName(const DefinedAtom *atom) { + assert(atom->sectionChoice() == DefinedAtom::sectionCustomRequired); + StringRef s = atom->customSectionName(); + size_t pos = s.find('$'); + return (pos == StringRef::npos) ? s : s.substr(0, pos); +} + +StringRef chooseSectionByContent(const DefinedAtom *atom) { + switch (atom->contentType()) { + case DefinedAtom::typeCode: + return ".text"; + case DefinedAtom::typeZeroFill: + return ".bss"; + case DefinedAtom::typeData: + if (atom->permissions() == DefinedAtom::permR__) + return ".rdata"; + if (atom->permissions() == DefinedAtom::permRW_) + return ".data"; + break; + default: + break; + } + llvm::errs() << "Atom: contentType=" << atom->contentType() + << " permission=" << atom->permissions() << "\n"; + llvm::report_fatal_error("Failed to choose section based on content"); +} + +typedef std::map<StringRef, std::vector<const DefinedAtom *> > AtomVectorMap; + +void groupAtoms(const PECOFFLinkingContext &ctx, const File &file, + AtomVectorMap &result) { + for (const DefinedAtom *atom : file.defined()) { + if (atom->sectionChoice() == DefinedAtom::sectionCustomRequired) { + StringRef section = customSectionName(atom); + result[ctx.getOutputSectionName(section)].push_back(atom); + continue; + } + if (atom->sectionChoice() == DefinedAtom::sectionBasedOnContent) { + StringRef section = chooseSectionByContent(atom); + result[ctx.getOutputSectionName(section)].push_back(atom); + continue; + } + llvm_unreachable("Unknown section choice"); + } +} + +static const DefinedAtom *findTLSUsedSymbol(const PECOFFLinkingContext &ctx, + const File &file) { + StringRef sym = ctx.decorateSymbol("_tls_used"); + for (const DefinedAtom *atom : file.defined()) + if (atom->name() == sym) + return atom; + return nullptr; +} + +// Create all chunks that consist of the output file. +template <class PEHeader> +void PECOFFWriter::build(const File &linkedFile) { + AtomVectorMap atoms; + groupAtoms(_ctx, linkedFile, atoms); + + // Create file chunks and add them to the list. + auto *dosStub = new DOSStubChunk(_ctx); + auto *peHeader = new PEHeaderChunk<PEHeader>(_ctx); + auto *dataDirectory = new DataDirectoryChunk(); + auto *sectionTable = new SectionHeaderTableChunk(); + auto *stringTable = new StringTableChunk(); + addChunk(dosStub); + addChunk(peHeader); + addChunk(dataDirectory); + addChunk(sectionTable); + addChunk(stringTable); + + // Create sections and add the atoms to them. + for (auto i : atoms) { + StringRef sectionName = i.first; + std::vector<const DefinedAtom *> &contents = i.second; + std::unique_ptr<SectionChunk> section( + new AtomChunk(_ctx, sectionName, contents)); + if (section->size() > 0) + addSectionChunk(std::move(section), sectionTable, stringTable); + } + + // Build atom to its RVA map. + for (std::unique_ptr<Chunk> &cp : _chunks) + if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp)) + chunk->buildAtomRvaMap(_atomRva); + + // We know the addresses of all defined atoms that needs to be + // relocated. So we can create the ".reloc" section which contains + // all the relocation sites. + if (_ctx.getBaseRelocationEnabled()) { + std::unique_ptr<SectionChunk> baseReloc(new BaseRelocChunk(_chunks, _ctx)); + if (baseReloc->size()) { + SectionChunk &ref = *baseReloc; + addSectionChunk(std::move(baseReloc), sectionTable, stringTable); + dataDirectory->setField(DataDirectoryIndex::BASE_RELOCATION_TABLE, + ref.getVirtualAddress(), ref.size()); + } + } + + setImageSizeOnDisk(); + + if (stringTable->size()) { + peHeader->setPointerToSymbolTable(stringTable->fileOffset()); + peHeader->setNumberOfSymbols(1); + } + + for (std::unique_ptr<Chunk> &chunk : _chunks) { + SectionChunk *section = dyn_cast<SectionChunk>(chunk.get()); + if (!section) + continue; + if (section->getSectionName() == ".text") { + peHeader->setBaseOfCode(section->getVirtualAddress()); + + // Find the virtual address of the entry point symbol if any. PECOFF spec + // says that entry point for dll images is optional, in which case it must + // be set to 0. + if (_ctx.hasEntry()) { + AtomChunk *atom = cast<AtomChunk>(section); + uint64_t entryPointAddress = + atom->getAtomVirtualAddress(_ctx.getEntrySymbolName()); + + if (entryPointAddress) { + // NOTE: ARM NT assumes a pure THUMB execution, so adjust the entry + // point accordingly + if (_ctx.getMachineType() == llvm::COFF::IMAGE_FILE_MACHINE_ARMNT) + entryPointAddress |= 1; + peHeader->setAddressOfEntryPoint(entryPointAddress); + } + } else { + peHeader->setAddressOfEntryPoint(0); + } + } + StringRef name = section->getSectionName(); + if (name == ".data") { + peHeader->setBaseOfData(section->getVirtualAddress()); + continue; + } + DataDirectoryIndex ignore = DataDirectoryIndex(-1); + DataDirectoryIndex idx = llvm::StringSwitch<DataDirectoryIndex>(name) + .Case(".pdata", DataDirectoryIndex::EXCEPTION_TABLE) + .Case(".rsrc", DataDirectoryIndex::RESOURCE_TABLE) + .Case(".idata.a", DataDirectoryIndex::IAT) + .Case(".idata.d", DataDirectoryIndex::IMPORT_TABLE) + .Case(".edata", DataDirectoryIndex::EXPORT_TABLE) + .Case(".loadcfg", DataDirectoryIndex::LOAD_CONFIG_TABLE) + .Case(".didat.d", DataDirectoryIndex::DELAY_IMPORT_DESCRIPTOR) + .Default(ignore); + if (idx == ignore) + continue; + dataDirectory->setField(idx, section->getVirtualAddress(), section->size()); + } + + if (const DefinedAtom *atom = findTLSUsedSymbol(_ctx, linkedFile)) { + dataDirectory->setField(DataDirectoryIndex::TLS_TABLE, _atomRva[atom], + 0x18); + } + + // Now that we know the size and file offset of sections. Set the file + // header accordingly. + peHeader->setSizeOfCode(calcSizeOfCode()); + peHeader->setSizeOfInitializedData(calcSizeOfInitializedData()); + peHeader->setSizeOfUninitializedData(calcSizeOfUninitializedData()); + peHeader->setNumberOfSections(_numSections); + peHeader->setSizeOfImage(_imageSizeInMemory); + peHeader->setSizeOfHeaders(sectionTable->fileOffset() + sectionTable->size()); +} + +std::error_code PECOFFWriter::writeFile(const File &linkedFile, + StringRef path) { + if (_ctx.is64Bit()) { + this->build<llvm::object::pe32plus_header>(linkedFile); + } else { + this->build<llvm::object::pe32_header>(linkedFile); + } + + uint64_t totalSize = + _chunks.back()->fileOffset() + _chunks.back()->onDiskSize(); + std::unique_ptr<llvm::FileOutputBuffer> buffer; + std::error_code ec = llvm::FileOutputBuffer::create( + path, totalSize, buffer, llvm::FileOutputBuffer::F_executable); + if (ec) + return ec; + + for (std::unique_ptr<Chunk> &chunk : _chunks) + chunk->write(buffer->getBufferStart() + chunk->fileOffset()); + applyAllRelocations(buffer->getBufferStart()); + reorderSEHTableEntries(buffer->getBufferStart()); + DEBUG(printAllAtomAddresses()); + + if (_ctx.isDll()) + writeImportLibrary(_ctx); + + return buffer->commit(); +} + +/// Apply relocations to the output file buffer. This two pass. In the first +/// pass, we visit all atoms to create a map from atom to its virtual +/// address. In the second pass, we visit all relocation references to fix +/// up addresses in the buffer. +void PECOFFWriter::applyAllRelocations(uint8_t *bufferStart) { + // Create the list of section start addresses. It's needed for + // relocations of SECREL type. + std::vector<uint64_t> sectionRva; + for (auto &cp : _chunks) + if (SectionChunk *section = dyn_cast<SectionChunk>(&*cp)) + sectionRva.push_back(section->getVirtualAddress()); + + uint64_t base = _ctx.getBaseAddress(); + for (auto &cp : _chunks) { + if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp)) { + switch (_ctx.getMachineType()) { + default: llvm_unreachable("unsupported machine type"); + case llvm::COFF::IMAGE_FILE_MACHINE_ARMNT: + chunk->applyRelocationsARM(bufferStart, _atomRva, sectionRva, base); + break; + case llvm::COFF::IMAGE_FILE_MACHINE_I386: + chunk->applyRelocationsX86(bufferStart, _atomRva, sectionRva, base); + break; + case llvm::COFF::IMAGE_FILE_MACHINE_AMD64: + chunk->applyRelocationsX64(bufferStart, _atomRva, sectionRva, base); + break; + } + } + } +} + +/// Print atom VAs. Used only for debugging. +void PECOFFWriter::printAllAtomAddresses() const { + for (auto &cp : _chunks) + if (AtomChunk *chunk = dyn_cast<AtomChunk>(&*cp)) + chunk->printAtomAddresses(_ctx.getBaseAddress()); +} + +void PECOFFWriter::reorderSEHTableEntries(uint8_t *bufferStart) { + auto machineType = _ctx.getMachineType(); + if (machineType == llvm::COFF::IMAGE_FILE_MACHINE_I386) + reorderSEHTableEntriesX86(bufferStart); + if (machineType == llvm::COFF::IMAGE_FILE_MACHINE_AMD64) + reorderSEHTableEntriesX64(bufferStart); +} + +/// It seems that the entries in .sxdata must be sorted. This function is called +/// after a COFF file image is created in memory and before it is written to +/// disk. It is safe to reorder entries at this stage because the contents of +/// the entries are RVAs and there's no reference to a .sxdata entry other than +/// to the beginning of the section. +void PECOFFWriter::reorderSEHTableEntriesX86(uint8_t *bufferStart) { + for (std::unique_ptr<Chunk> &chunk : _chunks) { + if (SectionChunk *section = dyn_cast<SectionChunk>(chunk.get())) { + if (section->getSectionName() == ".sxdata") { + int numEntries = section->size() / sizeof(ulittle32_t); + ulittle32_t *begin = reinterpret_cast<ulittle32_t *>(bufferStart + section->fileOffset()); + ulittle32_t *end = begin + numEntries; + std::sort(begin, end); + } + } + } +} + +/// The entries in .pdata must be sorted according to its BeginAddress field +/// value. It's safe to do it because of the same reason as .sxdata. +void PECOFFWriter::reorderSEHTableEntriesX64(uint8_t *bufferStart) { + for (std::unique_ptr<Chunk> &chunk : _chunks) { + if (SectionChunk *section = dyn_cast<SectionChunk>(chunk.get())) { + if (section->getSectionName() != ".pdata") + continue; + int numEntries = section->size() / sizeof(coff_runtime_function_x64); + coff_runtime_function_x64 *begin = + (coff_runtime_function_x64 *)(bufferStart + section->fileOffset()); + coff_runtime_function_x64 *end = begin + numEntries; + std::sort(begin, end, [](const coff_runtime_function_x64 &lhs, + const coff_runtime_function_x64 &rhs) { + return lhs.BeginAddress < rhs.BeginAddress; + }); + } + } +} + +void PECOFFWriter::addChunk(Chunk *chunk) { + _chunks.push_back(std::unique_ptr<Chunk>(chunk)); +} + +void PECOFFWriter::addSectionChunk(std::unique_ptr<SectionChunk> chunk, + SectionHeaderTableChunk *table, + StringTableChunk *stringTable) { + table->addSection(chunk.get()); + _numSections++; + + StringRef sectionName = chunk->getSectionName(); + if (sectionName.size() > llvm::COFF::NameSize) { + uint32_t stringTableOffset = stringTable->addSectionName(sectionName); + chunk->setStringTableOffset(stringTableOffset); + } + + // Compute and set the starting address of sections when loaded in + // memory. They are different from positions on disk because sections need + // to be sector-aligned on disk but page-aligned in memory. + _imageSizeInMemory = llvm::RoundUpToAlignment( + _imageSizeInMemory, chunk->memAlign()); + chunk->setVirtualAddress(_imageSizeInMemory); + _imageSizeInMemory = llvm::RoundUpToAlignment( + _imageSizeInMemory + chunk->size(), _ctx.getPageSize()); + _chunks.push_back(std::move(chunk)); +} + +void PECOFFWriter::setImageSizeOnDisk() { + for (auto &chunk : _chunks) { + // Compute and set the offset of the chunk in the output file. + _imageSizeOnDisk = + llvm::RoundUpToAlignment(_imageSizeOnDisk, chunk->align()); + chunk->setFileOffset(_imageSizeOnDisk); + _imageSizeOnDisk += chunk->onDiskSize(); + } +} + +uint64_t PECOFFWriter::calcSectionSize( + llvm::COFF::SectionCharacteristics sectionType) const { + uint64_t ret = 0; + for (auto &cp : _chunks) + if (SectionChunk *chunk = dyn_cast<SectionChunk>(&*cp)) + if (chunk->getCharacteristics() & sectionType) + ret += chunk->onDiskSize(); + return ret; +} + +} // end namespace pecoff + +std::unique_ptr<Writer> createWriterPECOFF(const PECOFFLinkingContext &info) { + return std::unique_ptr<Writer>(new pecoff::PECOFFWriter(info)); +} + +} // end namespace lld diff --git a/lib/ReaderWriter/YAML/CMakeLists.txt b/lib/ReaderWriter/YAML/CMakeLists.txt new file mode 100644 index 0000000000000..b955baa94202a --- /dev/null +++ b/lib/ReaderWriter/YAML/CMakeLists.txt @@ -0,0 +1,6 @@ +add_llvm_library(lldYAML + ReaderWriterYAML.cpp + LINK_LIBS + lldCore + LLVMSupport + ) diff --git a/lib/ReaderWriter/YAML/Makefile b/lib/ReaderWriter/YAML/Makefile new file mode 100644 index 0000000000000..739b6eae747a9 --- /dev/null +++ b/lib/ReaderWriter/YAML/Makefile @@ -0,0 +1,14 @@ +##===- lld/lib/ReaderWriter/YAML/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 := lldYAML +USEDLIBS = lldCore.a + +include $(LLD_LEVEL)/Makefile diff --git a/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp b/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp new file mode 100644 index 0000000000000..868b9497c4cc6 --- /dev/null +++ b/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp @@ -0,0 +1,1358 @@ +//===- lib/ReaderWriter/YAML/ReaderWriterYAML.cpp -------------------------===// +// +// 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/DefinedAtom.h" +#include "lld/Core/Error.h" +#include "lld/Core/File.h" +#include "lld/Core/LLVM.h" +#include "lld/Core/Reader.h" +#include "lld/Core/Reference.h" +#include "lld/Core/Simple.h" +#include "lld/Core/Writer.h" +#include "lld/ReaderWriter/YamlContext.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" +#include <memory> +#include <string> +#include <system_error> + +using llvm::yaml::MappingTraits; +using llvm::yaml::ScalarEnumerationTraits; +using llvm::yaml::ScalarTraits; +using llvm::yaml::IO; +using llvm::yaml::SequenceTraits; +using llvm::yaml::DocumentListTraits; + +using namespace lld; + +/// The conversion of Atoms to and from YAML uses LLVM's YAML I/O. This +/// file just defines template specializations on the lld types which control +/// how the mapping is done to and from YAML. + +namespace { + +/// Used when writing yaml files. +/// In most cases, atoms names are unambiguous, so references can just +/// use the atom name as the target (e.g. target: foo). But in a few +/// cases that does not work, so ref-names are added. These are labels +/// used only in yaml. The labels do not exist in the Atom model. +/// +/// One need for ref-names are when atoms have no user supplied name +/// (e.g. c-string literal). Another case is when two object files with +/// identically named static functions are merged (ld -r) into one object file. +/// In that case referencing the function by name is ambiguous, so a unique +/// ref-name is added. +class RefNameBuilder { +public: + RefNameBuilder(const lld::File &file) + : _collisionCount(0), _unnamedCounter(0) { + // visit all atoms + for (const lld::DefinedAtom *atom : file.defined()) { + // Build map of atoms names to detect duplicates + if (!atom->name().empty()) + buildDuplicateNameMap(*atom); + + // Find references to unnamed atoms and create ref-names for them. + for (const lld::Reference *ref : *atom) { + // create refname for any unnamed reference target + const lld::Atom *target = ref->target(); + if ((target != nullptr) && target->name().empty()) { + std::string storage; + llvm::raw_string_ostream buffer(storage); + buffer << llvm::format("L%03d", _unnamedCounter++); + StringRef newName = copyString(buffer.str()); + _refNames[target] = newName; + DEBUG_WITH_TYPE("WriterYAML", + llvm::dbgs() << "unnamed atom: creating ref-name: '" + << newName << "' (" + << (const void *)newName.data() << ", " + << newName.size() << ")\n"); + } + } + } + for (const lld::UndefinedAtom *undefAtom : file.undefined()) { + buildDuplicateNameMap(*undefAtom); + } + for (const lld::SharedLibraryAtom *shlibAtom : file.sharedLibrary()) { + buildDuplicateNameMap(*shlibAtom); + } + for (const lld::AbsoluteAtom *absAtom : file.absolute()) { + if (!absAtom->name().empty()) + buildDuplicateNameMap(*absAtom); + } + } + + void buildDuplicateNameMap(const lld::Atom &atom) { + assert(!atom.name().empty()); + NameToAtom::iterator pos = _nameMap.find(atom.name()); + if (pos != _nameMap.end()) { + // Found name collision, give each a unique ref-name. + std::string Storage; + llvm::raw_string_ostream buffer(Storage); + buffer << atom.name() << llvm::format(".%03d", ++_collisionCount); + StringRef newName = copyString(buffer.str()); + _refNames[&atom] = newName; + DEBUG_WITH_TYPE("WriterYAML", + llvm::dbgs() << "name collsion: creating ref-name: '" + << newName << "' (" + << (const void *)newName.data() + << ", " << newName.size() << ")\n"); + const lld::Atom *prevAtom = pos->second; + AtomToRefName::iterator pos2 = _refNames.find(prevAtom); + if (pos2 == _refNames.end()) { + // Only create ref-name for previous if none already created. + std::string Storage2; + llvm::raw_string_ostream buffer2(Storage2); + buffer2 << prevAtom->name() << llvm::format(".%03d", ++_collisionCount); + StringRef newName2 = copyString(buffer2.str()); + _refNames[prevAtom] = newName2; + DEBUG_WITH_TYPE("WriterYAML", + llvm::dbgs() << "name collsion: creating ref-name: '" + << newName2 << "' (" + << (const void *)newName2.data() << ", " + << newName2.size() << ")\n"); + } + } else { + // First time we've seen this name, just add it to map. + _nameMap[atom.name()] = &atom; + DEBUG_WITH_TYPE("WriterYAML", llvm::dbgs() + << "atom name seen for first time: '" + << atom.name() << "' (" + << (const void *)atom.name().data() + << ", " << atom.name().size() << ")\n"); + } + } + + bool hasRefName(const lld::Atom *atom) { return _refNames.count(atom); } + + StringRef refName(const lld::Atom *atom) { + return _refNames.find(atom)->second; + } + +private: + typedef llvm::StringMap<const lld::Atom *> NameToAtom; + typedef llvm::DenseMap<const lld::Atom *, std::string> AtomToRefName; + + // Allocate a new copy of this string in _storage, so the strings + // can be freed when RefNameBuilder is destroyed. + StringRef copyString(StringRef str) { + char *s = _storage.Allocate<char>(str.size()); + memcpy(s, str.data(), str.size()); + return StringRef(s, str.size()); + } + + unsigned int _collisionCount; + unsigned int _unnamedCounter; + NameToAtom _nameMap; + AtomToRefName _refNames; + llvm::BumpPtrAllocator _storage; +}; + +/// Used when reading yaml files to find the target of a reference +/// that could be a name or ref-name. +class RefNameResolver { +public: + RefNameResolver(const lld::File *file, IO &io); + + const lld::Atom *lookup(StringRef name) const { + NameToAtom::const_iterator pos = _nameMap.find(name); + if (pos != _nameMap.end()) + return pos->second; + _io.setError(Twine("no such atom name: ") + name); + return nullptr; + } + + /// \brief Lookup a group parent when there is a reference of type + /// kindGroupChild. If there was no group-parent produce an appropriate + /// error. + const lld::Atom *lookupGroupParent(StringRef name) const { + NameToAtom::const_iterator pos = _groupMap.find(name); + if (pos != _groupMap.end()) + return pos->second; + _io.setError(Twine("no such group name: ") + name); + return nullptr; + } + +private: + typedef llvm::StringMap<const lld::Atom *> NameToAtom; + + void add(StringRef name, const lld::Atom *atom) { + if (const lld::DefinedAtom *da = dyn_cast<DefinedAtom>(atom)) { + if (da->isGroupParent()) { + if (_groupMap.count(name)) { + _io.setError(Twine("duplicate group name: ") + name); + } else { + _groupMap[name] = atom; + } + return; + } + } + if (_nameMap.count(name)) { + _io.setError(Twine("duplicate atom name: ") + name); + } else { + _nameMap[name] = atom; + } + } + + IO &_io; + NameToAtom _nameMap; + NameToAtom _groupMap; +}; + +// Used in NormalizedFile to hold the atoms lists. +template <typename T> class AtomList : public lld::File::atom_collection<T> { +public: + virtual lld::File::atom_iterator<T> begin() const { + return lld::File::atom_iterator<T>( + *this, + _atoms.empty() ? 0 : reinterpret_cast<const void *>(_atoms.data())); + } + virtual lld::File::atom_iterator<T> end() const { + return lld::File::atom_iterator<T>( + *this, _atoms.empty() ? 0 : reinterpret_cast<const void *>( + _atoms.data() + _atoms.size())); + } + virtual const T *deref(const void *it) const { + return *reinterpret_cast<const T *const *>(it); + } + virtual void next(const void *&it) const { + const T *const *p = reinterpret_cast<const T *const *>(it); + ++p; + it = reinterpret_cast<const void *>(p); + } + virtual void push_back(const T *element) { _atoms.push_back(element); } + virtual uint64_t size() const { return _atoms.size(); } + std::vector<const T *> _atoms; +}; + +/// Mapping of kind: field in yaml files. +enum FileKinds { + fileKindObjectAtoms, // atom based object file encoded in yaml + fileKindArchive, // static archive library encoded in yaml + fileKindObjectELF, // ELF object files encoded in yaml + fileKindObjectMachO // mach-o object files encoded in yaml +}; + +struct ArchMember { + FileKinds _kind; + StringRef _name; + const lld::File *_content; +}; + +// The content bytes in a DefinedAtom are just uint8_t but we want +// special formatting, so define a strong type. +LLVM_YAML_STRONG_TYPEDEF(uint8_t, ImplicitHex8) + +// SharedLibraryAtoms have a bool canBeNull() method which we'd like to be +// more readable than just true/false. +LLVM_YAML_STRONG_TYPEDEF(bool, ShlibCanBeNull) + +// lld::Reference::Kind is a tuple of <namespace, arch, value>. +// For yaml, we just want one string that encapsulates the tuple. +struct RefKind { + Reference::KindNamespace ns; + Reference::KindArch arch; + Reference::KindValue value; +}; + +} // namespace anon + +LLVM_YAML_IS_SEQUENCE_VECTOR(ArchMember) +LLVM_YAML_IS_SEQUENCE_VECTOR(const lld::Reference *) +// Always write DefinedAtoms content bytes as a flow sequence. +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(ImplicitHex8) +// for compatibility with gcc-4.7 in C++11 mode, add extra namespace +namespace llvm { +namespace yaml { + +// This is a custom formatter for RefKind +template <> struct ScalarTraits<RefKind> { + static void output(const RefKind &kind, void *ctxt, raw_ostream &out) { + assert(ctxt != nullptr); + YamlContext *info = reinterpret_cast<YamlContext *>(ctxt); + assert(info->_registry); + StringRef str; + if (info->_registry->referenceKindToString(kind.ns, kind.arch, kind.value, + str)) + out << str; + else + out << (int)(kind.ns) << "-" << (int)(kind.arch) << "-" << kind.value; + } + + static StringRef input(StringRef scalar, void *ctxt, RefKind &kind) { + assert(ctxt != nullptr); + YamlContext *info = reinterpret_cast<YamlContext *>(ctxt); + assert(info->_registry); + if (info->_registry->referenceKindFromString(scalar, kind.ns, kind.arch, + kind.value)) + return StringRef(); + return StringRef("unknown reference kind"); + } + + static bool mustQuote(StringRef) { return false; } +}; + +template <> struct ScalarEnumerationTraits<lld::File::Kind> { + static void enumeration(IO &io, lld::File::Kind &value) { + io.enumCase(value, "object", lld::File::kindObject); + io.enumCase(value, "shared-library", lld::File::kindSharedLibrary); + io.enumCase(value, "static-library", lld::File::kindArchiveLibrary); + } +}; + +template <> struct ScalarEnumerationTraits<lld::Atom::Scope> { + static void enumeration(IO &io, lld::Atom::Scope &value) { + io.enumCase(value, "global", lld::Atom::scopeGlobal); + io.enumCase(value, "hidden", lld::Atom::scopeLinkageUnit); + io.enumCase(value, "static", lld::Atom::scopeTranslationUnit); + } +}; + +template <> struct ScalarEnumerationTraits<lld::DefinedAtom::SectionChoice> { + static void enumeration(IO &io, lld::DefinedAtom::SectionChoice &value) { + io.enumCase(value, "content", lld::DefinedAtom::sectionBasedOnContent); + io.enumCase(value, "custom", lld::DefinedAtom::sectionCustomPreferred); + io.enumCase(value, "custom-required", + lld::DefinedAtom::sectionCustomRequired); + } +}; + +template <> struct ScalarEnumerationTraits<lld::DefinedAtom::Interposable> { + static void enumeration(IO &io, lld::DefinedAtom::Interposable &value) { + io.enumCase(value, "no", DefinedAtom::interposeNo); + io.enumCase(value, "yes", DefinedAtom::interposeYes); + io.enumCase(value, "yes-and-weak", DefinedAtom::interposeYesAndRuntimeWeak); + } +}; + +template <> struct ScalarEnumerationTraits<lld::DefinedAtom::Merge> { + static void enumeration(IO &io, lld::DefinedAtom::Merge &value) { + io.enumCase(value, "no", lld::DefinedAtom::mergeNo); + io.enumCase(value, "as-tentative", lld::DefinedAtom::mergeAsTentative); + io.enumCase(value, "as-weak", lld::DefinedAtom::mergeAsWeak); + io.enumCase(value, "as-addressed-weak", + lld::DefinedAtom::mergeAsWeakAndAddressUsed); + io.enumCase(value, "by-content", lld::DefinedAtom::mergeByContent); + io.enumCase(value, "same-name-and-size", + lld::DefinedAtom::mergeSameNameAndSize); + io.enumCase(value, "largest", lld::DefinedAtom::mergeByLargestSection); + } +}; + +template <> struct ScalarEnumerationTraits<lld::DefinedAtom::DeadStripKind> { + static void enumeration(IO &io, lld::DefinedAtom::DeadStripKind &value) { + io.enumCase(value, "normal", lld::DefinedAtom::deadStripNormal); + io.enumCase(value, "never", lld::DefinedAtom::deadStripNever); + io.enumCase(value, "always", lld::DefinedAtom::deadStripAlways); + } +}; + +template <> struct ScalarEnumerationTraits<lld::DefinedAtom::DynamicExport> { + static void enumeration(IO &io, lld::DefinedAtom::DynamicExport &value) { + io.enumCase(value, "normal", lld::DefinedAtom::dynamicExportNormal); + io.enumCase(value, "always", lld::DefinedAtom::dynamicExportAlways); + } +}; + +template <> struct ScalarEnumerationTraits<lld::DefinedAtom::CodeModel> { + static void enumeration(IO &io, lld::DefinedAtom::CodeModel &value) { + io.enumCase(value, "none", lld::DefinedAtom::codeNA); + io.enumCase(value, "mips-pic", lld::DefinedAtom::codeMipsPIC); + io.enumCase(value, "mips-micro", lld::DefinedAtom::codeMipsMicro); + io.enumCase(value, "mips-micro-pic", lld::DefinedAtom::codeMipsMicroPIC); + io.enumCase(value, "mips-16", lld::DefinedAtom::codeMips16); + io.enumCase(value, "arm-thumb", lld::DefinedAtom::codeARMThumb); + } +}; + +template <> +struct ScalarEnumerationTraits<lld::DefinedAtom::ContentPermissions> { + static void enumeration(IO &io, lld::DefinedAtom::ContentPermissions &value) { + io.enumCase(value, "---", lld::DefinedAtom::perm___); + io.enumCase(value, "r--", lld::DefinedAtom::permR__); + io.enumCase(value, "r-x", lld::DefinedAtom::permR_X); + io.enumCase(value, "rw-", lld::DefinedAtom::permRW_); + io.enumCase(value, "rwx", lld::DefinedAtom::permRWX); + io.enumCase(value, "rw-l", lld::DefinedAtom::permRW_L); + io.enumCase(value, "unknown", lld::DefinedAtom::permUnknown); + } +}; + +template <> struct ScalarEnumerationTraits<lld::DefinedAtom::ContentType> { + static void enumeration(IO &io, lld::DefinedAtom::ContentType &value) { + io.enumCase(value, "unknown", DefinedAtom::typeUnknown); + io.enumCase(value, "code", DefinedAtom::typeCode); + io.enumCase(value, "stub", DefinedAtom::typeStub); + io.enumCase(value, "constant", DefinedAtom::typeConstant); + io.enumCase(value, "data", DefinedAtom::typeData); + io.enumCase(value, "quick-data", DefinedAtom::typeDataFast); + io.enumCase(value, "zero-fill", DefinedAtom::typeZeroFill); + io.enumCase(value, "zero-fill-quick", DefinedAtom::typeZeroFillFast); + io.enumCase(value, "const-data", DefinedAtom::typeConstData); + io.enumCase(value, "got", DefinedAtom::typeGOT); + io.enumCase(value, "resolver", DefinedAtom::typeResolver); + io.enumCase(value, "branch-island", DefinedAtom::typeBranchIsland); + io.enumCase(value, "branch-shim", DefinedAtom::typeBranchShim); + io.enumCase(value, "stub-helper", DefinedAtom::typeStubHelper); + io.enumCase(value, "c-string", DefinedAtom::typeCString); + io.enumCase(value, "utf16-string", DefinedAtom::typeUTF16String); + io.enumCase(value, "unwind-cfi", DefinedAtom::typeCFI); + io.enumCase(value, "unwind-lsda", DefinedAtom::typeLSDA); + io.enumCase(value, "const-4-byte", DefinedAtom::typeLiteral4); + io.enumCase(value, "const-8-byte", DefinedAtom::typeLiteral8); + io.enumCase(value, "const-16-byte", DefinedAtom::typeLiteral16); + io.enumCase(value, "lazy-pointer", DefinedAtom::typeLazyPointer); + io.enumCase(value, "lazy-dylib-pointer", + DefinedAtom::typeLazyDylibPointer); + io.enumCase(value, "cfstring", DefinedAtom::typeCFString); + io.enumCase(value, "initializer-pointer", + DefinedAtom::typeInitializerPtr); + io.enumCase(value, "terminator-pointer", + DefinedAtom::typeTerminatorPtr); + io.enumCase(value, "c-string-pointer",DefinedAtom::typeCStringPtr); + io.enumCase(value, "objc-class-pointer", + DefinedAtom::typeObjCClassPtr); + io.enumCase(value, "objc-category-list", + DefinedAtom::typeObjC2CategoryList); + io.enumCase(value, "objc-class1", DefinedAtom::typeObjC1Class); + io.enumCase(value, "dtraceDOF", DefinedAtom::typeDTraceDOF); + io.enumCase(value, "interposing-tuples", + DefinedAtom::typeInterposingTuples); + io.enumCase(value, "lto-temp", DefinedAtom::typeTempLTO); + io.enumCase(value, "compact-unwind", DefinedAtom::typeCompactUnwindInfo); + io.enumCase(value, "unwind-info", DefinedAtom::typeProcessedUnwindInfo); + io.enumCase(value, "tlv-thunk", DefinedAtom::typeThunkTLV); + io.enumCase(value, "tlv-data", DefinedAtom::typeTLVInitialData); + io.enumCase(value, "tlv-zero-fill", DefinedAtom::typeTLVInitialZeroFill); + io.enumCase(value, "tlv-initializer-ptr", + DefinedAtom::typeTLVInitializerPtr); + io.enumCase(value, "mach_header", DefinedAtom::typeMachHeader); + io.enumCase(value, "thread-data", DefinedAtom::typeThreadData); + io.enumCase(value, "thread-zero-fill",DefinedAtom::typeThreadZeroFill); + io.enumCase(value, "ro-note", DefinedAtom::typeRONote); + io.enumCase(value, "rw-note", DefinedAtom::typeRWNote); + io.enumCase(value, "no-alloc", DefinedAtom::typeNoAlloc); + io.enumCase(value, "group-comdat", DefinedAtom::typeGroupComdat); + io.enumCase(value, "gnu-linkonce", DefinedAtom::typeGnuLinkOnce); + } +}; + +template <> struct ScalarEnumerationTraits<lld::UndefinedAtom::CanBeNull> { + static void enumeration(IO &io, lld::UndefinedAtom::CanBeNull &value) { + io.enumCase(value, "never", lld::UndefinedAtom::canBeNullNever); + io.enumCase(value, "at-runtime", lld::UndefinedAtom::canBeNullAtRuntime); + io.enumCase(value, "at-buildtime",lld::UndefinedAtom::canBeNullAtBuildtime); + } +}; + +template <> struct ScalarEnumerationTraits<ShlibCanBeNull> { + static void enumeration(IO &io, ShlibCanBeNull &value) { + io.enumCase(value, "never", false); + io.enumCase(value, "at-runtime", true); + } +}; + +template <> +struct ScalarEnumerationTraits<lld::SharedLibraryAtom::Type> { + static void enumeration(IO &io, lld::SharedLibraryAtom::Type &value) { + io.enumCase(value, "code", lld::SharedLibraryAtom::Type::Code); + io.enumCase(value, "data", lld::SharedLibraryAtom::Type::Data); + io.enumCase(value, "unknown", lld::SharedLibraryAtom::Type::Unknown); + } +}; + +/// This is a custom formatter for lld::DefinedAtom::Alignment. Values look +/// like: +/// 2^3 # 8-byte aligned +/// 7 mod 2^4 # 16-byte aligned plus 7 bytes +template <> struct ScalarTraits<lld::DefinedAtom::Alignment> { + static void output(const lld::DefinedAtom::Alignment &value, void *ctxt, + raw_ostream &out) { + if (value.modulus == 0) { + out << llvm::format("2^%d", value.powerOf2); + } else { + out << llvm::format("%d mod 2^%d", value.modulus, value.powerOf2); + } + } + + static StringRef input(StringRef scalar, void *ctxt, + lld::DefinedAtom::Alignment &value) { + value.modulus = 0; + size_t modStart = scalar.find("mod"); + if (modStart != StringRef::npos) { + StringRef modStr = scalar.slice(0, modStart); + modStr = modStr.rtrim(); + unsigned int modulus; + if (modStr.getAsInteger(0, modulus)) { + return "malformed alignment modulus"; + } + value.modulus = modulus; + scalar = scalar.drop_front(modStart + 3); + scalar = scalar.ltrim(); + } + if (!scalar.startswith("2^")) { + return "malformed alignment"; + } + StringRef powerStr = scalar.drop_front(2); + unsigned int power; + if (powerStr.getAsInteger(0, power)) { + return "malformed alignment power"; + } + value.powerOf2 = power; + if (value.modulus > (1 << value.powerOf2)) { + return "malformed alignment, modulus too large for power"; + } + return StringRef(); // returning empty string means success + } + + static bool mustQuote(StringRef) { return false; } +}; + +template <> struct ScalarEnumerationTraits<FileKinds> { + static void enumeration(IO &io, FileKinds &value) { + io.enumCase(value, "object", fileKindObjectAtoms); + io.enumCase(value, "archive", fileKindArchive); + io.enumCase(value, "object-elf", fileKindObjectELF); + io.enumCase(value, "object-mach-o", fileKindObjectMachO); + } +}; + +template <> struct MappingTraits<ArchMember> { + static void mapping(IO &io, ArchMember &member) { + io.mapOptional("kind", member._kind, fileKindObjectAtoms); + io.mapOptional("name", member._name); + io.mapRequired("content", member._content); + } +}; + +// Declare that an AtomList is a yaml sequence. +template <typename T> struct SequenceTraits<AtomList<T> > { + static size_t size(IO &io, AtomList<T> &seq) { return seq._atoms.size(); } + static const T *&element(IO &io, AtomList<T> &seq, size_t index) { + if (index >= seq._atoms.size()) + seq._atoms.resize(index + 1); + return seq._atoms[index]; + } +}; + +// Used to allow DefinedAtom content bytes to be a flow sequence of +// two-digit hex numbers without the leading 0x (e.g. FF, 04, 0A) +template <> struct ScalarTraits<ImplicitHex8> { + static void output(const ImplicitHex8 &val, void *, raw_ostream &out) { + uint8_t num = val; + out << llvm::format("%02X", num); + } + + static StringRef input(StringRef str, void *, ImplicitHex8 &val) { + unsigned long long n; + if (getAsUnsignedInteger(str, 16, n)) + return "invalid two-digit-hex number"; + if (n > 0xFF) + return "out of range two-digit-hex number"; + val = n; + return StringRef(); // returning empty string means success + } + + static bool mustQuote(StringRef) { return false; } +}; + +// YAML conversion for std::vector<const lld::File*> +template <> struct DocumentListTraits<std::vector<const lld::File *> > { + static size_t size(IO &io, std::vector<const lld::File *> &seq) { + return seq.size(); + } + static const lld::File *&element(IO &io, std::vector<const lld::File *> &seq, + size_t index) { + if (index >= seq.size()) + seq.resize(index + 1); + return seq[index]; + } +}; + +// YAML conversion for const lld::File* +template <> struct MappingTraits<const lld::File *> { + + class NormArchiveFile : public lld::ArchiveLibraryFile { + public: + NormArchiveFile(IO &io) : ArchiveLibraryFile(""), _path() {} + NormArchiveFile(IO &io, const lld::File *file) + : ArchiveLibraryFile(file->path()), _path(file->path()) { + // If we want to support writing archives, this constructor would + // need to populate _members. + } + + const lld::File *denormalize(IO &io) { return this; } + + const atom_collection<lld::DefinedAtom> &defined() const override { + return _noDefinedAtoms; + } + const atom_collection<lld::UndefinedAtom> &undefined() const override { + return _noUndefinedAtoms; + } + virtual const atom_collection<lld::SharedLibraryAtom> & + sharedLibrary() const override { + return _noSharedLibraryAtoms; + } + const atom_collection<lld::AbsoluteAtom> &absolute() const override { + return _noAbsoluteAtoms; + } + File *find(StringRef name, bool dataSymbolOnly) override { + for (const ArchMember &member : _members) { + for (const lld::DefinedAtom *atom : member._content->defined()) { + if (name == atom->name()) { + if (!dataSymbolOnly) + return const_cast<File *>(member._content); + switch (atom->contentType()) { + case lld::DefinedAtom::typeData: + case lld::DefinedAtom::typeZeroFill: + return const_cast<File *>(member._content); + default: + break; + } + } + } + } + return nullptr; + } + + virtual std::error_code + parseAllMembers(std::vector<std::unique_ptr<File>> &result) override { + return std::error_code(); + } + + StringRef _path; + std::vector<ArchMember> _members; + }; + + class NormalizedFile : public lld::File { + public: + NormalizedFile(IO &io) : File("", kindObject), _io(io), _rnb(nullptr) {} + NormalizedFile(IO &io, const lld::File *file) + : File(file->path(), kindObject), _io(io), + _rnb(new RefNameBuilder(*file)), _path(file->path()) { + for (const lld::DefinedAtom *a : file->defined()) + _definedAtoms.push_back(a); + for (const lld::UndefinedAtom *a : file->undefined()) + _undefinedAtoms.push_back(a); + for (const lld::SharedLibraryAtom *a : file->sharedLibrary()) + _sharedLibraryAtoms.push_back(a); + for (const lld::AbsoluteAtom *a : file->absolute()) + _absoluteAtoms.push_back(a); + } + const lld::File *denormalize(IO &io); + + const atom_collection<lld::DefinedAtom> &defined() const override { + return _definedAtoms; + } + const atom_collection<lld::UndefinedAtom> &undefined() const override { + return _undefinedAtoms; + } + virtual const atom_collection<lld::SharedLibraryAtom> & + sharedLibrary() const override { + return _sharedLibraryAtoms; + } + const atom_collection<lld::AbsoluteAtom> &absolute() const override { + return _absoluteAtoms; + } + + // Allocate a new copy of this string in _storage, so the strings + // can be freed when File is destroyed. + StringRef copyString(StringRef str) { + char *s = _storage.Allocate<char>(str.size()); + memcpy(s, str.data(), str.size()); + return StringRef(s, str.size()); + } + + IO &_io; + std::unique_ptr<RefNameBuilder> _rnb; + StringRef _path; + AtomList<lld::DefinedAtom> _definedAtoms; + AtomList<lld::UndefinedAtom> _undefinedAtoms; + AtomList<lld::SharedLibraryAtom> _sharedLibraryAtoms; + AtomList<lld::AbsoluteAtom> _absoluteAtoms; + llvm::BumpPtrAllocator _storage; + }; + + static void mapping(IO &io, const lld::File *&file) { + YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); + assert(info != nullptr); + // Let any register tag handler process this. + if (info->_registry && info->_registry->handleTaggedDoc(io, file)) + return; + // If no registered handler claims this tag and there is no tag, + // grandfather in as "!native". + if (io.mapTag("!native", true) || io.mapTag("tag:yaml.org,2002:map")) + mappingAtoms(io, file); + } + + static void mappingAtoms(IO &io, const lld::File *&file) { + MappingNormalizationHeap<NormalizedFile, const lld::File *> keys(io, file); + YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); + assert(info != nullptr); + info->_file = keys.operator->(); + + io.mapOptional("path", keys->_path); + io.mapOptional("defined-atoms", keys->_definedAtoms); + io.mapOptional("undefined-atoms", keys->_undefinedAtoms); + io.mapOptional("shared-library-atoms", keys->_sharedLibraryAtoms); + io.mapOptional("absolute-atoms", keys->_absoluteAtoms); + } + + static void mappingArchive(IO &io, const lld::File *&file) { + MappingNormalizationHeap<NormArchiveFile, const lld::File *> keys(io, file); + + io.mapOptional("path", keys->_path); + io.mapOptional("members", keys->_members); + } +}; + +// YAML conversion for const lld::Reference* +template <> struct MappingTraits<const lld::Reference *> { + + class NormalizedReference : public lld::Reference { + public: + NormalizedReference(IO &io) + : lld::Reference(lld::Reference::KindNamespace::all, + lld::Reference::KindArch::all, 0), + _target(nullptr), _targetName(), _offset(0), _addend(0), _tag(0) {} + + NormalizedReference(IO &io, const lld::Reference *ref) + : lld::Reference(ref->kindNamespace(), ref->kindArch(), + ref->kindValue()), + _target(nullptr), _targetName(targetName(io, ref)), + _offset(ref->offsetInAtom()), _addend(ref->addend()), + _tag(ref->tag()) { + _mappedKind.ns = ref->kindNamespace(); + _mappedKind.arch = ref->kindArch(); + _mappedKind.value = ref->kindValue(); + } + + const lld::Reference *denormalize(IO &io) { + YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); + assert(info != nullptr); + typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile; + NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file); + if (!_targetName.empty()) + _targetName = f->copyString(_targetName); + DEBUG_WITH_TYPE("WriterYAML", llvm::dbgs() + << "created Reference to name: '" + << _targetName << "' (" + << (const void *)_targetName.data() + << ", " << _targetName.size() << ")\n"); + setKindNamespace(_mappedKind.ns); + setKindArch(_mappedKind.arch); + setKindValue(_mappedKind.value); + return this; + } + void bind(const RefNameResolver &); + static StringRef targetName(IO &io, const lld::Reference *ref); + + uint64_t offsetInAtom() const override { return _offset; } + const lld::Atom *target() const override { return _target; } + Addend addend() const override { return _addend; } + void setAddend(Addend a) override { _addend = a; } + void setTarget(const lld::Atom *a) override { _target = a; } + + const lld::Atom *_target; + StringRef _targetName; + uint32_t _offset; + Addend _addend; + RefKind _mappedKind; + uint32_t _tag; + }; + + static void mapping(IO &io, const lld::Reference *&ref) { + MappingNormalizationHeap<NormalizedReference, const lld::Reference *> keys( + io, ref); + + io.mapRequired("kind", keys->_mappedKind); + io.mapOptional("offset", keys->_offset); + io.mapOptional("target", keys->_targetName); + io.mapOptional("addend", keys->_addend, (lld::Reference::Addend)0); + io.mapOptional("tag", keys->_tag, 0u); + } +}; + +// YAML conversion for const lld::DefinedAtom* +template <> struct MappingTraits<const lld::DefinedAtom *> { + + class NormalizedAtom : public lld::DefinedAtom { + public: + NormalizedAtom(IO &io) + : _file(fileFromContext(io)), _name(), _refName(), _contentType(), + _alignment(0), _content(), _references(), _isGroupChild(false) { + static uint32_t ordinalCounter = 1; + _ordinal = ordinalCounter++; + } + NormalizedAtom(IO &io, const lld::DefinedAtom *atom) + : _file(fileFromContext(io)), _name(atom->name()), _refName(), + _scope(atom->scope()), _interpose(atom->interposable()), + _merge(atom->merge()), _contentType(atom->contentType()), + _alignment(atom->alignment()), _sectionChoice(atom->sectionChoice()), + _deadStrip(atom->deadStrip()), _dynamicExport(atom->dynamicExport()), + _codeModel(atom->codeModel()), + _permissions(atom->permissions()), _size(atom->size()), + _sectionName(atom->customSectionName()), + _sectionSize(atom->sectionSize()) { + for (const lld::Reference *r : *atom) + _references.push_back(r); + if (!atom->occupiesDiskSpace()) + return; + ArrayRef<uint8_t> cont = atom->rawContent(); + _content.reserve(cont.size()); + for (uint8_t x : cont) + _content.push_back(x); + } + const lld::DefinedAtom *denormalize(IO &io) { + YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); + assert(info != nullptr); + typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile; + NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file); + if (!_name.empty()) + _name = f->copyString(_name); + if (!_refName.empty()) + _refName = f->copyString(_refName); + if (!_sectionName.empty()) + _sectionName = f->copyString(_sectionName); + DEBUG_WITH_TYPE("WriterYAML", + llvm::dbgs() << "created DefinedAtom named: '" << _name + << "' (" << (const void *)_name.data() + << ", " << _name.size() << ")\n"); + return this; + } + void bind(const RefNameResolver &); + // Extract current File object from YAML I/O parsing context + const lld::File &fileFromContext(IO &io) { + YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); + assert(info != nullptr); + assert(info->_file != nullptr); + return *info->_file; + } + + const lld::File &file() const override { return _file; } + StringRef name() const override { return _name; } + uint64_t size() const override { return _size; } + Scope scope() const override { return _scope; } + Interposable interposable() const override { return _interpose; } + Merge merge() const override { return _merge; } + ContentType contentType() const override { return _contentType; } + Alignment alignment() const override { return _alignment; } + SectionChoice sectionChoice() const override { return _sectionChoice; } + StringRef customSectionName() const override { return _sectionName; } + uint64_t sectionSize() const override { return _sectionSize; } + DeadStripKind deadStrip() const override { return _deadStrip; } + DynamicExport dynamicExport() const override { return _dynamicExport; } + CodeModel codeModel() const override { return _codeModel; } + ContentPermissions permissions() const override { return _permissions; } + void setGroupChild(bool val) { _isGroupChild = val; } + bool isGroupChild() const { return _isGroupChild; } + ArrayRef<uint8_t> rawContent() const override { + if (!occupiesDiskSpace()) + return ArrayRef<uint8_t>(); + return ArrayRef<uint8_t>( + reinterpret_cast<const uint8_t *>(_content.data()), _content.size()); + } + + uint64_t ordinal() const override { return _ordinal; } + + reference_iterator begin() const override { + uintptr_t index = 0; + const void *it = reinterpret_cast<const void *>(index); + return reference_iterator(*this, it); + } + reference_iterator end() const override { + uintptr_t index = _references.size(); + const void *it = reinterpret_cast<const void *>(index); + return reference_iterator(*this, it); + } + const lld::Reference *derefIterator(const void *it) const override { + uintptr_t index = reinterpret_cast<uintptr_t>(it); + assert(index < _references.size()); + return _references[index]; + } + void incrementIterator(const void *&it) const override { + uintptr_t index = reinterpret_cast<uintptr_t>(it); + ++index; + it = reinterpret_cast<const void *>(index); + } + + const lld::File &_file; + StringRef _name; + StringRef _refName; + Scope _scope; + Interposable _interpose; + Merge _merge; + ContentType _contentType; + Alignment _alignment; + SectionChoice _sectionChoice; + DeadStripKind _deadStrip; + DynamicExport _dynamicExport; + CodeModel _codeModel; + ContentPermissions _permissions; + uint32_t _ordinal; + std::vector<ImplicitHex8> _content; + uint64_t _size; + StringRef _sectionName; + uint64_t _sectionSize; + std::vector<const lld::Reference *> _references; + bool _isGroupChild; + }; + + static void mapping(IO &io, const lld::DefinedAtom *&atom) { + MappingNormalizationHeap<NormalizedAtom, const lld::DefinedAtom *> keys( + io, atom); + if (io.outputting()) { + // If writing YAML, check if atom needs a ref-name. + typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile; + YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); + assert(info != nullptr); + NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file); + assert(f); + assert(f->_rnb); + if (f->_rnb->hasRefName(atom)) { + keys->_refName = f->_rnb->refName(atom); + } + } + + io.mapOptional("name", keys->_name, StringRef()); + io.mapOptional("ref-name", keys->_refName, StringRef()); + io.mapOptional("scope", keys->_scope, + DefinedAtom::scopeTranslationUnit); + io.mapOptional("type", keys->_contentType, + DefinedAtom::typeCode); + io.mapOptional("content", keys->_content); + io.mapOptional("size", keys->_size, (uint64_t)keys->_content.size()); + io.mapOptional("interposable", keys->_interpose, + DefinedAtom::interposeNo); + io.mapOptional("merge", keys->_merge, DefinedAtom::mergeNo); + io.mapOptional("alignment", keys->_alignment, + DefinedAtom::Alignment(0)); + io.mapOptional("section-choice", keys->_sectionChoice, + DefinedAtom::sectionBasedOnContent); + io.mapOptional("section-name", keys->_sectionName, StringRef()); + io.mapOptional("section-size", keys->_sectionSize, (uint64_t)0); + io.mapOptional("dead-strip", keys->_deadStrip, + DefinedAtom::deadStripNormal); + io.mapOptional("dynamic-export", keys->_dynamicExport, + DefinedAtom::dynamicExportNormal); + io.mapOptional("code-model", keys->_codeModel, DefinedAtom::codeNA); + // default permissions based on content type + io.mapOptional("permissions", keys->_permissions, + DefinedAtom::permissions( + keys->_contentType)); + io.mapOptional("references", keys->_references); + } +}; + +// YAML conversion for const lld::UndefinedAtom* +template <> struct MappingTraits<const lld::UndefinedAtom *> { + + class NormalizedAtom : public lld::UndefinedAtom { + public: + NormalizedAtom(IO &io) + : _file(fileFromContext(io)), _name(), _canBeNull(canBeNullNever), + _fallback(nullptr) {} + + NormalizedAtom(IO &io, const lld::UndefinedAtom *atom) + : _file(fileFromContext(io)), _name(atom->name()), + _canBeNull(atom->canBeNull()), _fallback(atom->fallback()) {} + + const lld::UndefinedAtom *denormalize(IO &io) { + YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); + assert(info != nullptr); + typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile; + NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file); + if (!_name.empty()) + _name = f->copyString(_name); + + DEBUG_WITH_TYPE("WriterYAML", + llvm::dbgs() << "created UndefinedAtom named: '" << _name + << "' (" << (const void *)_name.data() << ", " + << _name.size() << ")\n"); + return this; + } + + // Extract current File object from YAML I/O parsing context + const lld::File &fileFromContext(IO &io) { + YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); + assert(info != nullptr); + assert(info->_file != nullptr); + return *info->_file; + } + + const lld::File &file() const override { return _file; } + StringRef name() const override { return _name; } + CanBeNull canBeNull() const override { return _canBeNull; } + const UndefinedAtom *fallback() const override { return _fallback; } + + const lld::File &_file; + StringRef _name; + CanBeNull _canBeNull; + const UndefinedAtom *_fallback; + }; + + static void mapping(IO &io, const lld::UndefinedAtom *&atom) { + MappingNormalizationHeap<NormalizedAtom, const lld::UndefinedAtom *> keys( + io, atom); + + io.mapRequired("name", keys->_name); + io.mapOptional("can-be-null", keys->_canBeNull, + lld::UndefinedAtom::canBeNullNever); + io.mapOptional("fallback", keys->_fallback, + (const lld::UndefinedAtom *)nullptr); + } +}; + +// YAML conversion for const lld::SharedLibraryAtom* +template <> struct MappingTraits<const lld::SharedLibraryAtom *> { + + class NormalizedAtom : public lld::SharedLibraryAtom { + public: + NormalizedAtom(IO &io) + : _file(fileFromContext(io)), _name(), _loadName(), _canBeNull(false), + _type(Type::Unknown), _size(0) {} + NormalizedAtom(IO &io, const lld::SharedLibraryAtom *atom) + : _file(fileFromContext(io)), _name(atom->name()), + _loadName(atom->loadName()), _canBeNull(atom->canBeNullAtRuntime()), + _type(atom->type()), _size(atom->size()) {} + + const lld::SharedLibraryAtom *denormalize(IO &io) { + YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); + assert(info != nullptr); + typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile; + NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file); + if (!_name.empty()) + _name = f->copyString(_name); + if (!_loadName.empty()) + _loadName = f->copyString(_loadName); + + DEBUG_WITH_TYPE("WriterYAML", + llvm::dbgs() << "created SharedLibraryAtom named: '" + << _name << "' (" + << (const void *)_name.data() + << ", " << _name.size() << ")\n"); + return this; + } + + // Extract current File object from YAML I/O parsing context + const lld::File &fileFromContext(IO &io) { + YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); + assert(info != nullptr); + assert(info->_file != nullptr); + return *info->_file; + } + + const lld::File &file() const override { return _file; } + StringRef name() const override { return _name; } + StringRef loadName() const override { return _loadName; } + bool canBeNullAtRuntime() const override { return _canBeNull; } + Type type() const override { return _type; } + uint64_t size() const override { return _size; } + + const lld::File &_file; + StringRef _name; + StringRef _loadName; + ShlibCanBeNull _canBeNull; + Type _type; + uint64_t _size; + }; + + static void mapping(IO &io, const lld::SharedLibraryAtom *&atom) { + + MappingNormalizationHeap<NormalizedAtom, const lld::SharedLibraryAtom *> + keys(io, atom); + + io.mapRequired("name", keys->_name); + io.mapOptional("load-name", keys->_loadName); + io.mapOptional("can-be-null", keys->_canBeNull, (ShlibCanBeNull) false); + io.mapOptional("type", keys->_type, SharedLibraryAtom::Type::Code); + io.mapOptional("size", keys->_size, uint64_t(0)); + } +}; + +// YAML conversion for const lld::AbsoluteAtom* +template <> struct MappingTraits<const lld::AbsoluteAtom *> { + + class NormalizedAtom : public lld::AbsoluteAtom { + public: + NormalizedAtom(IO &io) + : _file(fileFromContext(io)), _name(), _scope(), _value(0) {} + NormalizedAtom(IO &io, const lld::AbsoluteAtom *atom) + : _file(fileFromContext(io)), _name(atom->name()), + _scope(atom->scope()), _value(atom->value()) {} + const lld::AbsoluteAtom *denormalize(IO &io) { + YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); + assert(info != nullptr); + typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile; + NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file); + if (!_name.empty()) + _name = f->copyString(_name); + + DEBUG_WITH_TYPE("WriterYAML", + llvm::dbgs() << "created AbsoluteAtom named: '" << _name + << "' (" << (const void *)_name.data() + << ", " << _name.size() << ")\n"); + return this; + } + // Extract current File object from YAML I/O parsing context + const lld::File &fileFromContext(IO &io) { + YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); + assert(info != nullptr); + assert(info->_file != nullptr); + return *info->_file; + } + + const lld::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; } + + const lld::File &_file; + StringRef _name; + StringRef _refName; + Scope _scope; + Hex64 _value; + }; + + static void mapping(IO &io, const lld::AbsoluteAtom *&atom) { + MappingNormalizationHeap<NormalizedAtom, const lld::AbsoluteAtom *> keys( + io, atom); + + if (io.outputting()) { + typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile; + YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); + assert(info != nullptr); + NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file); + assert(f); + assert(f->_rnb); + if (f->_rnb->hasRefName(atom)) { + keys->_refName = f->_rnb->refName(atom); + } + } + + io.mapRequired("name", keys->_name); + io.mapOptional("ref-name", keys->_refName, StringRef()); + io.mapOptional("scope", keys->_scope); + io.mapRequired("value", keys->_value); + } +}; + +} // namespace llvm +} // namespace yaml + +RefNameResolver::RefNameResolver(const lld::File *file, IO &io) : _io(io) { + typedef MappingTraits<const lld::DefinedAtom *>::NormalizedAtom + NormalizedAtom; + for (const lld::DefinedAtom *a : file->defined()) { + const auto *na = (const NormalizedAtom *)a; + if (!na->_refName.empty()) + add(na->_refName, a); + else if (!na->_name.empty()) + add(na->_name, a); + } + + for (const lld::UndefinedAtom *a : file->undefined()) + add(a->name(), a); + + for (const lld::SharedLibraryAtom *a : file->sharedLibrary()) + add(a->name(), a); + + typedef MappingTraits<const lld::AbsoluteAtom *>::NormalizedAtom NormAbsAtom; + for (const lld::AbsoluteAtom *a : file->absolute()) { + const auto *na = (const NormAbsAtom *)a; + if (na->_refName.empty()) + add(na->_name, a); + else + add(na->_refName, a); + } +} + +inline const lld::File * +MappingTraits<const lld::File *>::NormalizedFile::denormalize(IO &io) { + typedef MappingTraits<const lld::DefinedAtom *>::NormalizedAtom + NormalizedAtom; + + RefNameResolver nameResolver(this, io); + // Now that all atoms are parsed, references can be bound. + for (const lld::DefinedAtom *a : this->defined()) { + auto *normAtom = (NormalizedAtom *)const_cast<DefinedAtom *>(a); + normAtom->bind(nameResolver); + } + + _definedAtoms._atoms.erase( + std::remove_if(_definedAtoms._atoms.begin(), _definedAtoms._atoms.end(), + [](const DefinedAtom *a) { + return ((const NormalizedAtom *)a)->isGroupChild(); + }), + _definedAtoms._atoms.end()); + + return this; +} + +inline void MappingTraits<const lld::DefinedAtom *>::NormalizedAtom::bind( + const RefNameResolver &resolver) { + typedef MappingTraits<const lld::Reference *>::NormalizedReference + NormalizedReference; + for (const lld::Reference *ref : _references) { + auto *normRef = (NormalizedReference *)const_cast<Reference *>(ref); + normRef->bind(resolver); + } +} + +inline void MappingTraits<const lld::Reference *>::NormalizedReference::bind( + const RefNameResolver &resolver) { + typedef MappingTraits<const lld::DefinedAtom *>::NormalizedAtom NormalizedAtom; + + _target = resolver.lookup(_targetName); + + if (_mappedKind.ns == lld::Reference::KindNamespace::all && + _mappedKind.value == lld::Reference::kindGroupChild) { + ((NormalizedAtom *)const_cast<Atom *>(_target))->setGroupChild(true); + } +} + +inline StringRef +MappingTraits<const lld::Reference *>::NormalizedReference::targetName( + IO &io, const lld::Reference *ref) { + if (ref->target() == nullptr) + return StringRef(); + YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext()); + assert(info != nullptr); + typedef MappingTraits<const lld::File *>::NormalizedFile NormalizedFile; + NormalizedFile *f = reinterpret_cast<NormalizedFile *>(info->_file); + RefNameBuilder &rnb = *f->_rnb; + if (rnb.hasRefName(ref->target())) + return rnb.refName(ref->target()); + return ref->target()->name(); +} + +namespace lld { +namespace yaml { + +class Writer : public lld::Writer { +public: + Writer(const LinkingContext &context) : _context(context) {} + + std::error_code writeFile(const lld::File &file, StringRef outPath) override { + // Create stream to path. + std::error_code ec; + llvm::raw_fd_ostream out(outPath, ec, llvm::sys::fs::F_Text); + if (ec) + return ec; + + // Create yaml Output writer, using yaml options for context. + YamlContext yamlContext; + yamlContext._linkingContext = &_context; + yamlContext._registry = &_context.registry(); + llvm::yaml::Output yout(out, &yamlContext); + + // Write yaml output. + const lld::File *fileRef = &file; + yout << fileRef; + + return std::error_code(); + } + +private: + const LinkingContext &_context; +}; + +} // end namespace yaml + +namespace { + +/// Handles !native tagged yaml documents. +class NativeYamlIOTaggedDocumentHandler : public YamlIOTaggedDocumentHandler { + bool handledDocTag(llvm::yaml::IO &io, const lld::File *&file) const override { + if (io.mapTag("!native")) { + MappingTraits<const lld::File *>::mappingAtoms(io, file); + return true; + } + return false; + } +}; + + +/// Handles !archive tagged yaml documents. +class ArchiveYamlIOTaggedDocumentHandler : public YamlIOTaggedDocumentHandler { + bool handledDocTag(llvm::yaml::IO &io, const lld::File *&file) const override { + if (io.mapTag("!archive")) { + MappingTraits<const lld::File *>::mappingArchive(io, file); + return true; + } + return false; + } +}; + + + +class YAMLReader : public Reader { +public: + YAMLReader(const Registry ®istry) : _registry(registry) {} + + bool canParse(file_magic, StringRef ext, const MemoryBuffer &) const override { + return (ext.equals(".objtxt") || ext.equals(".yaml")); + } + + std::error_code + loadFile(std::unique_ptr<MemoryBuffer> mb, const class Registry &, + std::vector<std::unique_ptr<File>> &result) const override { + // Create YAML Input Reader. + YamlContext yamlContext; + yamlContext._registry = &_registry; + yamlContext._path = mb->getBufferIdentifier(); + llvm::yaml::Input yin(mb->getBuffer(), &yamlContext); + + // Fill vector with File objects created by parsing yaml. + std::vector<const lld::File *> createdFiles; + yin >> createdFiles; + + // Error out now if there were parsing errors. + if (yin.error()) + return make_error_code(lld::YamlReaderError::illegal_value); + + std::shared_ptr<MemoryBuffer> smb(mb.release()); + for (const File *file : createdFiles) { + // Note: loadFile() should return vector of *const* File + File *f = const_cast<File *>(file); + f->setLastError(std::error_code()); + f->setSharedMemoryBuffer(smb); + result.emplace_back(f); + } + return make_error_code(lld::YamlReaderError::success); + } + +private: + const Registry &_registry; +}; + +} // anonymous namespace + +void Registry::addSupportYamlFiles() { + add(std::unique_ptr<Reader>(new YAMLReader(*this))); + add(std::unique_ptr<YamlIOTaggedDocumentHandler>( + new NativeYamlIOTaggedDocumentHandler())); + add(std::unique_ptr<YamlIOTaggedDocumentHandler>( + new ArchiveYamlIOTaggedDocumentHandler())); +} + +std::unique_ptr<Writer> createWriterYAML(const LinkingContext &context) { + return std::unique_ptr<Writer>(new lld::yaml::Writer(context)); +} + +} // end namespace lld diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000000000..e29f5f4c5a90b --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,47 @@ +set(LLVM_SOURCE_DIR "${LLVM_MAIN_SRC_DIR}") +set(LLVM_BINARY_DIR "${LLVM_BINARY_DIR}") +set(LLVM_BUILD_MODE "%(build_mode)s") +set(LLVM_TOOLS_DIR "${LLVM_TOOLS_BINARY_DIR}/%(build_config)s") +set(LLVM_LIBS_DIR "${LLVM_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}/%(build_config)s") +set(CLANG_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..") +set(CLANG_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/..") +if(BUILD_SHARED_LIBS) + set(ENABLE_SHARED 1) +else() + set(ENABLE_SHARED 0) +endif(BUILD_SHARED_LIBS) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg + ) + +set(LLD_TEST_DEPS + FileCheck not llvm-nm + lld llvm-config llvm-objdump llvm-readobj yaml2obj obj2yaml + linker-script-test macho-dump llvm-mc llvm-nm + ) +if (LLVM_INCLUDE_TESTS) + set(LLD_TEST_DEPS ${LLD_TEST_DEPS} LLDUnitTests) +endif() + +set(LLD_TEST_PARAMS + lld_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg + ) + +add_lit_testsuite(check-lld "Running lld test suite" + ${CMAKE_CURRENT_BINARY_DIR} + PARAMS lld_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg + lld_unit_site_config=${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg + DEPENDS ${LLD_TEST_DEPS} + ) + +set_target_properties(check-lld PROPERTIES FOLDER "lld tests") + +# Add a legacy target spelling: lld-test +add_custom_target(lld-test) +add_dependencies(lld-test check-lld) +set_target_properties(lld-test PROPERTIES FOLDER "lld tests") diff --git a/test/Driver/Inputs/libtest.a b/test/Driver/Inputs/libtest.a new file mode 100644 index 0000000000000..8b277f0dd5dcd --- /dev/null +++ b/test/Driver/Inputs/libtest.a @@ -0,0 +1 @@ +!<arch> diff --git a/test/Driver/Inputs/usr/lib/i386/libtest.a b/test/Driver/Inputs/usr/lib/i386/libtest.a new file mode 100644 index 0000000000000..8b277f0dd5dcd --- /dev/null +++ b/test/Driver/Inputs/usr/lib/i386/libtest.a @@ -0,0 +1 @@ +!<arch> diff --git a/test/Driver/Inputs/usr/lib/libtest.a b/test/Driver/Inputs/usr/lib/libtest.a new file mode 100644 index 0000000000000..8b277f0dd5dcd --- /dev/null +++ b/test/Driver/Inputs/usr/lib/libtest.a @@ -0,0 +1 @@ +!<arch> diff --git a/test/Driver/def-lib-search.test b/test/Driver/def-lib-search.test new file mode 100644 index 0000000000000..818cbfe6ad611 --- /dev/null +++ b/test/Driver/def-lib-search.test @@ -0,0 +1,8 @@ +# Check that search paths explicitly provided by the -L option +# are used in search before default paths. + +RUN: not lld -flavor gnu -target x86_64 -t -ltest \ +RUN: --sysroot=%p/Inputs -L%p/Inputs 2> %t +RUN: FileCheck %s < %t + +CHECK: {{[^ ]+}}{{[\\/]}}Inputs{{[\\/]}}libtest.a diff --git a/test/Driver/flavor-option.test b/test/Driver/flavor-option.test new file mode 100644 index 0000000000000..2ca3b410cbe2c --- /dev/null +++ b/test/Driver/flavor-option.test @@ -0,0 +1,8 @@ +# a) the -flavor option is position independent and does not need to be the 1st +# argument in the command line (bug 20975); +# b) UniversalDriver correctly removes -flavor along with its value and the +# underlying linker does not get a corrupted command line (bug 20977). +RUN: lld --help -flavor gnu | FileCheck %s + +CHECK: --noinhibit-exec +CHECK: --output-filetype diff --git a/test/Driver/lib-search.test b/test/Driver/lib-search.test new file mode 100644 index 0000000000000..9d3666f63c36b --- /dev/null +++ b/test/Driver/lib-search.test @@ -0,0 +1,24 @@ +RUN: not lld -flavor gnu -t -ltest -L%p/Inputs 2> %t.err +RUN: FileCheck %s < %t.err + +RUN: not lld -flavor gnu -target x86_64--netbsd -t -ltest \ +RUN: --sysroot=%p/Inputs 2> %t2 +RUN: FileCheck -check-prefix=NETBSD-AMD64 %s < %t2 +RUN: not lld -flavor gnu -target x86_64--netbsd -nostdlib -t -ltest \ +RUN: --sysroot=%p/Inputs 2> %t3 +RUN: FileCheck -check-prefix=NETBSD-AMD64-NS %s < %t3 +RUN: not lld -flavor gnu -target i386--netbsd -t -ltest \ +RUN: --sysroot=%p/Inputs 2> %t4 +RUN: FileCheck -check-prefix=NETBSD-I386 %s < %t4 +RUN: not lld -flavor gnu -target x86_64--netbsd -m elf_i386 -t -ltest \ +RUN: --sysroot=%p/Inputs 2> %t5 +RUN: FileCheck -check-prefix=NETBSD-AMD64_32 %s < %t5 + +# run linker with -t mode to dump full paths to input files + +CHECK: {{[^ ]+[\\/]}}Inputs{{[\\/]}}libtest.a + +NETBSD-AMD64: {{[^ ]+}}{{[\\/]}}Inputs{{[\\/]}}usr{{[\\/]}}lib{{[\\/]}}libtest.a +NETBSD-AMD64-NS-NOT: {{[^ ]+}}{{[\\/]}}Inputs{{[\\/]}}usr{{[\\/]}}lib{{[\\/]}}libtest.a +NETBSD-I386: {{[^ ]+}}{{[\\/]}}Inputs{{[\\/]}}usr{{[\\/]}}lib{{[\\/]}}libtest.a +NETBSD-AMD64_32: {{[^ ]+}}{{[\\/]}}Inputs{{[\\/]}}usr{{[\\/]}}lib{{[\\/]}}i386{{[\\/]}}libtest.a diff --git a/test/Driver/so-whole-archive.test b/test/Driver/so-whole-archive.test new file mode 100644 index 0000000000000..0732c35a7a922 --- /dev/null +++ b/test/Driver/so-whole-archive.test @@ -0,0 +1,63 @@ +# Check that LLD does not show any error if the --whole-archive +# is around non-archive. + +# RUN: yaml2obj -format=elf -docnum=1 %s > %t-so.o +# RUN: yaml2obj -format=elf -docnum=2 %s > %t-exe.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o +# RUN: lld -flavor gnu -target mipsel -o %t.exe %t-exe.o --whole-archive %t.so + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x04 +Symbols: + Global: + - Name: foo + Type: STT_FUNC + Section: .text + Size: 0x04 + +# exe.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x04 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: foo + Type: R_MIPS_26 + +Symbols: + Global: + - Name: __start + Type: STT_FUNC + Section: .text + Size: 0x04 + - Name: foo +... diff --git a/test/Driver/trivial-driver.test b/test/Driver/trivial-driver.test new file mode 100644 index 0000000000000..196d6cfa9d4d6 --- /dev/null +++ b/test/Driver/trivial-driver.test @@ -0,0 +1,5 @@ +# This test, tests the Gnu lld option --help +RUN: lld -flavor gnu --help | FileCheck %s + +CHECK: --noinhibit-exec +CHECK: --output-filetype diff --git a/test/Driver/undef-basic.objtxt b/test/Driver/undef-basic.objtxt new file mode 100644 index 0000000000000..f942d5c8e9040 --- /dev/null +++ b/test/Driver/undef-basic.objtxt @@ -0,0 +1,22 @@ +# RUN: lld -flavor gnu -u undefinedsymbol -e entrysymbol %s \ +# RUN: --output-filetype=yaml --noinhibit-exec | FileCheck %s + +# +# Test that we are able to add undefined atoms from the command line +# + +--- +absolute-atoms: + - name: putchar + value: 0xFFFF0040 + + - name: reset + value: 0xFFFF0080 + +... + + +# CHECK: undefined-atoms: +# CHECK: - name: entrysymbol +# CHECK: - name: undefinedsymbol +# CHECK: can-be-null: at-buildtime diff --git a/test/LinkerScript/expr-precedence.test b/test/LinkerScript/expr-precedence.test new file mode 100644 index 0000000000000..5170e34ba4b36 --- /dev/null +++ b/test/LinkerScript/expr-precedence.test @@ -0,0 +1,34 @@ +/* + RUN: linker-script-test %s | FileCheck %s +*/ +SECTIONS { + . = foo >= bar + 1 ? bar : 1- - - -1; +} + +/* +CHECK: kw_sections: SECTIONS +CHECK: l_brace: { +CHECK: identifier: . +CHECK: equal: = +CHECK: identifier: foo +CHECK: greaterequal: >= +CHECK: identifier: bar +CHECK: plus: + +CHECK: number: 1 +CHECK: question: ? +CHECK: identifier: bar +CHECK: colon: : +CHECK: number: 1 +CHECK: minus: - +CHECK: minus: - +CHECK: minus: - +CHECK: minus: - +CHECK: number: 1 +CHECK: semicolon: ; +CHECK: r_brace: } +CHECK: eof: +CHECK: SECTIONS +CHECK: { +CHECK: . = (foo >= (bar + 1)) ? bar : (1 - (-(-(-1)))) +CHECK: } +*/ diff --git a/test/LinkerScript/extern-bad-symbol.test b/test/LinkerScript/extern-bad-symbol.test new file mode 100644 index 0000000000000..279a7cc205670 --- /dev/null +++ b/test/LinkerScript/extern-bad-symbol.test @@ -0,0 +1,22 @@ +/* + RUN: linker-script-test %s 2> %t | FileCheck %s + RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s +*/ + + +EXTERN(a b 3) +/* +CHECK-ERR: [[@LINE-2]]:12: error: expected symbol in EXTERN. +CHECK-ERR-NEXT: {{^EXTERN\(a b 3\)}} +CHECK-ERR-NEXT: {{^ \^}} +*/ + +/* +CHECK: kw_extern: EXTERN +CHECK: l_paren: ( +CHECK: identifier: a +CHECK: identifier: b +CHECK: number: 3 +CHECK: r_paren: ) +CHECK: eof: +*/ diff --git a/test/LinkerScript/extern-empty.test b/test/LinkerScript/extern-empty.test new file mode 100644 index 0000000000000..a5e1ece084d9e --- /dev/null +++ b/test/LinkerScript/extern-empty.test @@ -0,0 +1,19 @@ +/* + RUN: linker-script-test %s 2> %t | FileCheck %s + RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s +*/ + + +EXTERN() +/* +CHECK-ERR: [[@LINE-2]]:8: error: expected one or more symbols in EXTERN. +CHECK-ERR-NEXT: {{^EXTERN()}} +CHECK-ERR-NEXT: {{^ \^}} +*/ + +/* +CHECK: kw_extern: EXTERN +CHECK: l_paren: ( +CHECK: r_paren: ) +CHECK: eof: +*/ diff --git a/test/LinkerScript/extern-valid.test b/test/LinkerScript/extern-valid.test new file mode 100644 index 0000000000000..764b4668a34fa --- /dev/null +++ b/test/LinkerScript/extern-valid.test @@ -0,0 +1,29 @@ +/* + RUN: linker-script-test %s | FileCheck %s +*/ + +EXTERN(a) +EXTERN(a b) +EXTERN(_foo _bar _baz) + +/* +CHECK: kw_extern: EXTERN +CHECK: l_paren: ( +CHECK: identifier: a +CHECK: r_paren: ) +CHECK: kw_extern: EXTERN +CHECK: l_paren: ( +CHECK: identifier: a +CHECK: identifier: b +CHECK: r_paren: ) +CHECK: kw_extern: EXTERN +CHECK: l_paren: ( +CHECK: identifier: _foo +CHECK: identifier: _bar +CHECK: identifier: _baz +CHECK: r_paren: ) +CHECK: eof: +CHECK: EXTERN(a) +CHECK: EXTERN(a b) +CHECK: EXTERN(_foo _bar _baz) +*/ diff --git a/test/LinkerScript/incomplete-ternary.test b/test/LinkerScript/incomplete-ternary.test new file mode 100644 index 0000000000000..fae30537d2de6 --- /dev/null +++ b/test/LinkerScript/incomplete-ternary.test @@ -0,0 +1,25 @@ +/* + RUN: linker-script-test %s 2> %t | FileCheck %s + RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s +*/ +SECTIONS { + . = foo ? bar; +/* +CHECK-ERR: [[@LINE-2]]:18: error: expected : +CHECK-ERR-NEXT: {{^ \. = foo \? bar;}} +CHECK-ERR-NEXT: {{^ \^}} +*/ +} + +/* +CHECK: kw_sections: SECTIONS +CHECK: l_brace: { +CHECK: identifier: . +CHECK: equal: = +CHECK: identifier: foo +CHECK: question: ? +CHECK: identifier: bar +CHECK: semicolon: ; +CHECK: r_brace: } +CHECK: eof: +*/ diff --git a/test/LinkerScript/libname-err-1.test b/test/LinkerScript/libname-err-1.test new file mode 100644 index 0000000000000..451a469336bdd --- /dev/null +++ b/test/LinkerScript/libname-err-1.test @@ -0,0 +1,11 @@ +/* RUN: linker-script-test %s 2>&1 | FileCheck %s +*/ + +OUTPUT_ARCH(i386:x86_64) +OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") +GROUP( -l### ) +ENTRY(init) + +/* +CHECK: libname-err-1.test:6:10: error: expected ) +*/ diff --git a/test/LinkerScript/libname-err-2.test b/test/LinkerScript/libname-err-2.test new file mode 100644 index 0000000000000..f1d96d7181516 --- /dev/null +++ b/test/LinkerScript/libname-err-2.test @@ -0,0 +1,11 @@ +/* RUN: linker-script-test %s 2>&1 | FileCheck %s +*/ + +OUTPUT_ARCH(i386:x86_64) +OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") +GROUP( -l ) +ENTRY(init) + +/* +CHECK: libname-err-2.test:6:10: error: expected ) +*/ diff --git a/test/LinkerScript/linker-script-outputformat.test b/test/LinkerScript/linker-script-outputformat.test new file mode 100644 index 0000000000000..b47bb38ad8d8e --- /dev/null +++ b/test/LinkerScript/linker-script-outputformat.test @@ -0,0 +1,12 @@ +/* RUN: linker-script-test %s | FileCheck %s +*/ + +OUTPUT_FORMAT(elf64-x86-64) + +/* +CHECK: kw_output_format: OUTPUT_FORMAT +CHECK: l_paren: ( +CHECK: identifier: elf64-x86-64 +CHECK: r_paren: ) +CHECK: OUTPUT_FORMAT("elf64-x86-64") +*/ diff --git a/test/LinkerScript/linker-script.test b/test/LinkerScript/linker-script.test new file mode 100644 index 0000000000000..421493666e849 --- /dev/null +++ b/test/LinkerScript/linker-script.test @@ -0,0 +1,46 @@ +/* RUN: linker-script-test %s | FileCheck %s +*/ + +OUTPUT_ARCH(i386:x86_64) +OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") +OUTPUT("/out/foo") +GROUP ( /lib/x86_64-linux-gnu/libc.so.6 /usr/lib/x86_64-linux-gnu/libc_nonshared.a AS_NEEDED ( /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 ) -lm -l:libgcc.a ) +ENTRY(init) + +/* +CHECK: kw_output_arch: OUTPUT_ARCH +CHECK: l_paren: ( +CHECK: identifier: i386:x86_64 +CHECK: r_paren: ) +CHECK: kw_output_format: OUTPUT_FORMAT +CHECK: l_paren: ( +CHECK: identifier: elf64-x86-64 +CHECK: comma: , +CHECK: identifier: elf64-x86-64 +CHECK: comma: , +CHECK: identifier: elf64-x86-64 +CHECK: r_paren: ) +CHECK: kw_output: OUTPUT +CHECK: l_paren: ( +CHECK: identifier: /out/foo +CHECK: r_paren: ) +CHECK: kw_group: GROUP +CHECK: l_paren: ( +CHECK: identifier: /lib/x86_64-linux-gnu/libc.so.6 +CHECK: identifier: /usr/lib/x86_64-linux-gnu/libc_nonshared.a +CHECK: kw_as_needed: AS_NEEDED +CHECK: l_paren: ( +CHECK: identifier: /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 +CHECK: r_paren: ) +CHECK: libname: m +CHECK: libname: :libgcc.a +CHECK: r_paren: ) +CHECK: kw_entry: ENTRY +CHECK: l_paren: ( +CHECK: identifier: init +CHECK: r_paren: ) +CHECK: eof: +CHECK: OUTPUT_FORMAT("elf64-x86-64","elf64-x86-64","elf64-x86-64") +CHECK: GROUP(/lib/x86_64-linux-gnu/libc.so.6 /usr/lib/x86_64-linux-gnu/libc_nonshared.a AS_NEEDED(/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2) -lm -l:libgcc.a) +CHECK: ENTRY(init) +*/ diff --git a/test/LinkerScript/memory-empty.test b/test/LinkerScript/memory-empty.test new file mode 100644 index 0000000000000..b71022afdff3e --- /dev/null +++ b/test/LinkerScript/memory-empty.test @@ -0,0 +1,17 @@ +/* + RUN: linker-script-test %s | FileCheck %s +*/ + +MEMORY +{ +} + +/* +CHECK: kw_memory: MEMORY +CHECK: l_brace: { +CHECK: r_brace: } +CHECK: eof: +CHECK: MEMORY +CHECK: { +CHECK: } +*/ diff --git a/test/LinkerScript/memory-missing-attrs.test b/test/LinkerScript/memory-missing-attrs.test new file mode 100644 index 0000000000000..fbbf38b765143 --- /dev/null +++ b/test/LinkerScript/memory-missing-attrs.test @@ -0,0 +1,32 @@ +/* + RUN: linker-script-test %s 2> %t | FileCheck %s + RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s +*/ + +MEMORY +{ + ram () : ORIGIN = 0x20000000, LENGTH = 128M +/* +CHECK-ERR: [[@LINE-2]]:8: error: Expected memory attribute string. +CHECK-ERR-NEXT: {{^ ram \(\) : ORIGIN = 0x20000000, LENGTH = 128M}} +CHECK-ERR-NEXT: {{^ \^}} +*/ +} + +/* +CHECK: kw_memory: MEMORY +CHECK: l_brace: { +CHECK: identifier: ram +CHECK: l_paren: ( +CHECK: r_paren: ) +CHECK: colon: : +CHECK: kw_origin: ORIGIN +CHECK: equal: = +CHECK: number: 0x20000000 +CHECK: comma: , +CHECK: kw_length: LENGTH +CHECK: equal: = +CHECK: number: 128M +CHECK: r_brace: } +CHECK: eof: +*/ diff --git a/test/LinkerScript/memory-missing-length.test b/test/LinkerScript/memory-missing-length.test new file mode 100644 index 0000000000000..f317c9a267882 --- /dev/null +++ b/test/LinkerScript/memory-missing-length.test @@ -0,0 +1,29 @@ +/* + RUN: linker-script-test %s 2> %t | FileCheck %s + RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s +*/ + +MEMORY +{ + ram (rwx) : ORIGIN = 0x20000000, +} +/* +CHECK-ERR: [[@LINE-2]]:1: error: expected LENGTH +CHECK-ERR-NEXT: {{^}}} +CHECK-ERR-NEXT: {{^\^}} +*/ + +/* +CHECK: kw_memory: MEMORY +CHECK: l_brace: { +CHECK: identifier: ram +CHECK: l_paren: ( +CHECK: identifier: rwx +CHECK: r_paren: ) +CHECK: colon: : +CHECK: kw_origin: ORIGIN +CHECK: equal: = +CHECK: number: 0x20000000 +CHECK: r_brace: } +CHECK: eof: +*/ diff --git a/test/LinkerScript/memory-missing-name.test b/test/LinkerScript/memory-missing-name.test new file mode 100644 index 0000000000000..46597d4351d36 --- /dev/null +++ b/test/LinkerScript/memory-missing-name.test @@ -0,0 +1,31 @@ +/* + RUN: linker-script-test %s 2> %t | FileCheck %s + RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s +*/ + +MEMORY +{ + (rwx) : ORIGIN = 0x20000000, LENGTH = 128M +/* +CHECK-ERR: [[@LINE-2]]:3: error: expected memory block definition. +CHECK-ERR-NEXT: {{^ \(rwx\) : ORIGIN = 0x20000000, LENGTH = 128M}} +CHECK-ERR-NEXT: {{^ \^}} +*/ +} + +/* +CHECK: kw_memory: MEMORY +CHECK: l_brace: { +CHECK: l_paren: ( +CHECK: r_paren: ) +CHECK: colon: : +CHECK: kw_origin: ORIGIN +CHECK: equal: = +CHECK: number: 0x20000000 +CHECK: comma: , +CHECK: kw_length: LENGTH +CHECK: equal: = +CHECK: number: 128M +CHECK: r_brace: } +CHECK: eof: +*/ diff --git a/test/LinkerScript/memory-missing-origin.test b/test/LinkerScript/memory-missing-origin.test new file mode 100644 index 0000000000000..50a64d17d6c54 --- /dev/null +++ b/test/LinkerScript/memory-missing-origin.test @@ -0,0 +1,30 @@ +/* + RUN: linker-script-test %s 2> %t | FileCheck %s + RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s +*/ + +MEMORY +{ + ram (rwx) : LENGTH = 128M +/* +CHECK-ERR: [[@LINE-2]]:15: error: expected ORIGIN +CHECK-ERR-NEXT: {{^ ram \(rwx\) : LENGTH = 128M}} +CHECK-ERR-NEXT: {{^ \^}} +*/ + +} + +/* +CHECK: kw_memory: MEMORY +CHECK: l_brace: { +CHECK: identifier: ram +CHECK: l_paren: ( +CHECK: identifier: rwx +CHECK: r_paren: ) +CHECK: colon: : +CHECK: kw_length: LENGTH +CHECK: equal: = +CHECK: number: 128M +CHECK: r_brace: } +CHECK: eof: +*/ diff --git a/test/LinkerScript/memory-valid.test b/test/LinkerScript/memory-valid.test new file mode 100644 index 0000000000000..7a0e906c5fb59 --- /dev/null +++ b/test/LinkerScript/memory-valid.test @@ -0,0 +1,56 @@ +/* + RUN: linker-script-test %s | FileCheck %s +*/ + +MEMORY +{ + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 96K + rom (rx) : org = 0x0, len = 256K + boot : o = 0x1000000, l = 0x5f00 +} + +/* +CHECK: kw_memory: MEMORY +CHECK: l_brace: { +CHECK: identifier: ram +CHECK: l_paren: ( +CHECK: identifier: rwx +CHECK: r_paren: ) +CHECK: colon: : +CHECK: kw_origin: ORIGIN +CHECK: equal: = +CHECK: number: 0x20000000 +CHECK: comma: , +CHECK: kw_length: LENGTH +CHECK: equal: = +CHECK: number: 96K +CHECK: identifier: rom +CHECK: l_paren: ( +CHECK: identifier: rx +CHECK: r_paren: ) +CHECK: colon: : +CHECK: kw_origin: org +CHECK: equal: = +CHECK: number: 0x0 +CHECK: comma: , +CHECK: kw_length: len +CHECK: equal: = +CHECK: number: 256K +CHECK: identifier: boot +CHECK: colon: : +CHECK: kw_origin: o +CHECK: equal: = +CHECK: number: 0x1000000 +CHECK: comma: , +CHECK: kw_length: l +CHECK: equal: = +CHECK: number: 0x5f00 +CHECK: r_brace: } +CHECK: eof: +CHECK: MEMORY +CHECK: { +CHECK: ram (rwx) : ORIGIN = 536870912, LENGTH = 98304 +CHECK: rom (rx) : ORIGIN = 0, LENGTH = 262144 +CHECK: boot : ORIGIN = 16777216, LENGTH = 24320 +CHECK: } +*/ diff --git a/test/LinkerScript/missing-entry-symbol.test b/test/LinkerScript/missing-entry-symbol.test new file mode 100644 index 0000000000000..54e8f75c90a49 --- /dev/null +++ b/test/LinkerScript/missing-entry-symbol.test @@ -0,0 +1,21 @@ +/* + RUN: linker-script-test %s 2> %t | FileCheck %s + RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s +*/ +SECTIONS { + ENTRY() +/* +CHECK-ERR: [[@LINE-2]]:11: error: expected identifier in ENTRY +CHECK-ERR-NEXT: {{^ ENTRY()}} +CHECK-ERR-NEXT: {{^ \^}} +*/ +} + +/* +CHECK: l_brace: { +CHECK: kw_entry: ENTRY +CHECK: l_paren: ( +CHECK: r_paren: ) +CHECK: r_brace: } +CHECK: eof: +*/ diff --git a/test/LinkerScript/missing-input-file-name.test b/test/LinkerScript/missing-input-file-name.test new file mode 100644 index 0000000000000..c4218ccef321e --- /dev/null +++ b/test/LinkerScript/missing-input-file-name.test @@ -0,0 +1,25 @@ +/* + RUN: linker-script-test %s 2> %t | FileCheck %s + RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s +*/ +SECTIONS { + .text : { ()} +/* +CHECK-ERR: [[@LINE-2]]:15: error: expected symbol assignment or input file name. +CHECK-ERR-NEXT: {{^ \.text : { \(\)}}} +CHECK-ERR-NEXT: {{^ \^}} +*/ +} + +/* +CHECK: kw_sections: SECTIONS +CHECK: l_brace: { +CHECK: identifier: .text +CHECK: colon: : +CHECK: l_brace: { +CHECK: l_paren: ( +CHECK: r_paren: ) +CHECK: r_brace: } +CHECK: r_brace: } +CHECK: eof: +*/ diff --git a/test/LinkerScript/missing-input-sections.test b/test/LinkerScript/missing-input-sections.test new file mode 100644 index 0000000000000..5db93444b1a9a --- /dev/null +++ b/test/LinkerScript/missing-input-sections.test @@ -0,0 +1,27 @@ +/* + RUN: linker-script-test %s 2> %t | FileCheck %s + RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s +*/ +SECTIONS { + .text : { *(+)} +/* +CHECK-ERR: [[@LINE-2]]:16: error: expected ) +CHECK-ERR-NEXT: {{^ \.text : { \*\(\+\)}}} +CHECK-ERR-NEXT: {{^ \^}} +*/ +} + +/* +CHECK: kw_sections: SECTIONS +CHECK: l_brace: { +CHECK: identifier: .text +CHECK: colon: : +CHECK: l_brace: { +CHECK: star: * +CHECK: l_paren: ( +CHECK: plus: + +CHECK: r_paren: ) +CHECK: r_brace: } +CHECK: r_brace: } +CHECK: eof: +*/ diff --git a/test/LinkerScript/missing-operand.test b/test/LinkerScript/missing-operand.test new file mode 100644 index 0000000000000..cfa87bf407687 --- /dev/null +++ b/test/LinkerScript/missing-operand.test @@ -0,0 +1,24 @@ +/* + RUN: linker-script-test %s 2> %t | FileCheck %s + RUN: FileCheck -check-prefix=CHECK-ERR -input-file %t %s +*/ +SECTIONS { + . = foo / ; +/* +CHECK-ERR: [[@LINE-2]]:15: error: expected symbol, number, minus, tilde or left parenthesis. +CHECK-ERR-NEXT: {{^ . = foo / ;}} +CHECK-ERR-NEXT: {{^ \^}} +*/ +} + +/* +CHECK: kw_sections: SECTIONS +CHECK: l_brace: { +CHECK: identifier: . +CHECK: equal: = +CHECK: identifier: foo +CHECK: slash: / +CHECK: semicolon: ; +CHECK: r_brace: } +CHECK: eof: + */ diff --git a/test/LinkerScript/missing-output-section-name.test b/test/LinkerScript/missing-output-section-name.test new file mode 100644 index 0000000000000..79792982b6190 --- /dev/null +++ b/test/LinkerScript/missing-output-section-name.test @@ -0,0 +1,25 @@ +/* + RUN: linker-script-test %s 2> %t | FileCheck %s + RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s +*/ +SECTIONS { + : { *()} +/* +CHECK-ERR: [[@LINE-2]]:5: error: expected symbol assignment, entry, overlay or output section name +CHECK-ERR-NEXT: {{^ : { \*\(\)}}} +CHECK-ERR-NEXT: {{^ \^}} +*/ +} + +/* +CHECK: kw_sections: SECTIONS +CHECK: l_brace: { +CHECK: colon: : +CHECK: l_brace: { +CHECK: star: * +CHECK: l_paren: ( +CHECK: r_paren: ) +CHECK: r_brace: } +CHECK: r_brace: } +CHECK: eof: +*/ diff --git a/test/LinkerScript/missing-symbol.test b/test/LinkerScript/missing-symbol.test new file mode 100644 index 0000000000000..4e8777ef40b92 --- /dev/null +++ b/test/LinkerScript/missing-symbol.test @@ -0,0 +1,24 @@ +/* + RUN: linker-script-test %s 2> %t | FileCheck %s + RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s +*/ +SECTIONS { + = foo + bar; +/* +CHECK-ERR: [[@LINE-2]]:5: error: expected symbol assignment, entry, overlay or output section name. +CHECK-ERR-NEXT: {{^ = foo \+ bar;}} +CHECK-ERR-NEXT: {{^ \^}} +*/ +} + +/* +CHECK: kw_sections: SECTIONS +CHECK: l_brace: { +CHECK: equal: = +CHECK: identifier: foo +CHECK: plus: + +CHECK: identifier: bar +CHECK: semicolon: ; +CHECK: r_brace: } +CHECK: eof: +*/ diff --git a/test/LinkerScript/sections.test b/test/LinkerScript/sections.test new file mode 100644 index 0000000000000..8f7d01418044f --- /dev/null +++ b/test/LinkerScript/sections.test @@ -0,0 +1,618 @@ +/* + This test exercises parsing typical commands found in GNU ld linker scripts. + RUN: linker-script-test %s | FileCheck %s +*/ + +SEARCH_DIR("/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); +SECTIONS +{ + PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS; + .interp : { *(.interp) } + .note.gnu.build-id : { *(.note.gnu.build-id) } + .hash : { *(.hash) } + .rela.dyn : + { + *(.rela.init) + *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) + *(.rela.fini) + *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) + } + .rela.plt : + { + *(.rela.plt) + PROVIDE_HIDDEN (__rela_iplt_start = .); + *(.rela.iplt) + PROVIDE_HIDDEN (__rela_iplt_end = .); + } + .init : + { + KEEP (*(SORT_NONE(.init))) + } =0x909090909090909090909090 + PROVIDE (__etext = .); + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) } + .exception_ranges : ONLY_IF_RO { *(.exception_ranges + .exception_ranges*) } + . = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1)); . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); + /* Exception handling */ + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) } + .ctors : + { + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .); + .got.plt : { *(.got.plt) *(.igot.plt) } + .lrodata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : + { + *(.lrodata .lrodata.* .gnu.linkonce.lr.*) + } + .ldata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : + { + *(.ldata .ldata.* .gnu.linkonce.l.*) + . = ALIGN(. != 0 ? 64 / 8 : 1); + } + . = ALIGN(64 / 8); + _end = .; PROVIDE (end = .); + . = DATA_SEGMENT_END (.); + /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } +} + +/* +CHECK: kw_search_dir: SEARCH_DIR +CHECK: l_paren: ( +CHECK: identifier: /usr/x86_64-linux-gnu/lib64 +CHECK: r_paren: ) +CHECK: semicolon: ; +CHECK: kw_search_dir: SEARCH_DIR +CHECK: l_paren: ( +CHECK: identifier: =/usr/local/lib/x86_64-linux-gnu +CHECK: r_paren: ) +CHECK: semicolon: ; +CHECK: kw_sections: SECTIONS +CHECK: l_brace: { +CHECK: kw_provide: PROVIDE +CHECK: l_paren: ( +CHECK: identifier: __executable_start +CHECK: equal: = +CHECK: identifier: SEGMENT_START +CHECK: l_paren: ( +CHECK: identifier: text-segment +CHECK: comma: , +CHECK: number: 0x400000 +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: semicolon: ; +CHECK: identifier: . +CHECK: equal: = +CHECK: identifier: SEGMENT_START +CHECK: l_paren: ( +CHECK: identifier: text-segment +CHECK: comma: , +CHECK: number: 0x400000 +CHECK: r_paren: ) +CHECK: plus: + +CHECK: identifier: SIZEOF_HEADERS +CHECK: semicolon: ; +CHECK: identifier: .interp +CHECK: colon: : +CHECK: l_brace: { +CHECK: star: * +CHECK: l_paren: ( +CHECK: identifier: .interp +CHECK: r_paren: ) +CHECK: r_brace: } +CHECK: identifier: .note.gnu.build-id +CHECK: colon: : +CHECK: l_brace: { +CHECK: star: * +CHECK: l_paren: ( +CHECK: identifier: .note.gnu.build-id +CHECK: r_paren: ) +CHECK: r_brace: } +CHECK: identifier: .hash +CHECK: colon: : +CHECK: l_brace: { +CHECK: star: * +CHECK: l_paren: ( +CHECK: identifier: .hash +CHECK: r_paren: ) +CHECK: r_brace: } +CHECK: identifier: .rela.dyn +CHECK: colon: : +CHECK: l_brace: { +CHECK: star: * +CHECK: l_paren: ( +CHECK: identifier: .rela.init +CHECK: r_paren: ) +CHECK: star: * +CHECK: l_paren: ( +CHECK: identifier: .rela.text +CHECK: identifier: .rela.text.* +CHECK: identifier: .rela.gnu.linkonce.t.* +CHECK: r_paren: ) +CHECK: star: * +CHECK: l_paren: ( +CHECK: identifier: .rela.fini +CHECK: r_paren: ) +CHECK: star: * +CHECK: l_paren: ( +CHECK: identifier: .rela.rodata +CHECK: identifier: .rela.rodata.* +CHECK: identifier: .rela.gnu.linkonce.r.* +CHECK: r_paren: ) +CHECK: r_brace: } +CHECK: identifier: .rela.plt +CHECK: colon: : +CHECK: l_brace: { +CHECK: star: * +CHECK: l_paren: ( +CHECK: identifier: .rela.plt +CHECK: r_paren: ) +CHECK: kw_provide_hidden: PROVIDE_HIDDEN +CHECK: l_paren: ( +CHECK: identifier: __rela_iplt_start +CHECK: equal: = +CHECK: identifier: . +CHECK: r_paren: ) +CHECK: semicolon: ; +CHECK: star: * +CHECK: l_paren: ( +CHECK: identifier: .rela.iplt +CHECK: r_paren: ) +CHECK: kw_provide_hidden: PROVIDE_HIDDEN +CHECK: l_paren: ( +CHECK: identifier: __rela_iplt_end +CHECK: equal: = +CHECK: identifier: . +CHECK: r_paren: ) +CHECK: semicolon: ; +CHECK: r_brace: } +CHECK: identifier: .init +CHECK: colon: : +CHECK: l_brace: { +CHECK: kw_keep: KEEP +CHECK: l_paren: ( +CHECK: star: * +CHECK: l_paren: ( +CHECK: kw_sort_none: SORT_NONE +CHECK: l_paren: ( +CHECK: identifier: .init +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: r_brace: } +CHECK: kw_provide: PROVIDE +CHECK: l_paren: ( +CHECK: identifier: __etext +CHECK: equal: = +CHECK: identifier: . +CHECK: r_paren: ) +CHECK: semicolon: ; +CHECK: identifier: .eh_frame +CHECK: colon: : +CHECK: kw_only_if_ro: ONLY_IF_RO +CHECK: l_brace: { +CHECK: kw_keep: KEEP +CHECK: l_paren: ( +CHECK: star: * +CHECK: l_paren: ( +CHECK: identifier: .eh_frame +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: r_brace: } +CHECK: identifier: .exception_ranges +CHECK: colon: : +CHECK: kw_only_if_ro: ONLY_IF_RO +CHECK: l_brace: { +CHECK: star: * +CHECK: l_paren: ( +CHECK: identifier: .exception_ranges +CHECK: identifier: .exception_ranges* +CHECK: r_paren: ) +CHECK: r_brace: } +CHECK: identifier: . +CHECK: equal: = +CHECK: kw_align: ALIGN +CHECK: l_paren: ( +CHECK: identifier: CONSTANT +CHECK: l_paren: ( +CHECK: identifier: MAXPAGESIZE +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: minus: - +CHECK: l_paren: ( +CHECK: l_paren: ( +CHECK: identifier: CONSTANT +CHECK: l_paren: ( +CHECK: identifier: MAXPAGESIZE +CHECK: r_paren: ) +CHECK: minus: - +CHECK: identifier: . +CHECK: r_paren: ) +CHECK: amp: & +CHECK: l_paren: ( +CHECK: identifier: CONSTANT +CHECK: l_paren: ( +CHECK: identifier: MAXPAGESIZE +CHECK: r_paren: ) +CHECK: minus: - +CHECK: number: 1 +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: semicolon: ; +CHECK: identifier: . +CHECK: equal: = +CHECK: identifier: DATA_SEGMENT_ALIGN +CHECK: l_paren: ( +CHECK: identifier: CONSTANT +CHECK: l_paren: ( +CHECK: identifier: MAXPAGESIZE +CHECK: r_paren: ) +CHECK: comma: , +CHECK: identifier: CONSTANT +CHECK: l_paren: ( +CHECK: identifier: COMMONPAGESIZE +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: semicolon: ; +CHECK: identifier: .eh_frame +CHECK: colon: : +CHECK: kw_only_if_rw: ONLY_IF_RW +CHECK: l_brace: { +CHECK: kw_keep: KEEP +CHECK: l_paren: ( +CHECK: star: * +CHECK: l_paren: ( +CHECK: identifier: .eh_frame +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: r_brace: } +CHECK: identifier: .ctors +CHECK: colon: : +CHECK: l_brace: { +CHECK: kw_keep: KEEP +CHECK: l_paren: ( +CHECK: identifier: *crtbegin.o +CHECK: l_paren: ( +CHECK: identifier: .ctors +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: kw_keep: KEEP +CHECK: l_paren: ( +CHECK: identifier: *crtbegin?.o +CHECK: l_paren: ( +CHECK: identifier: .ctors +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: kw_keep: KEEP +CHECK: l_paren: ( +CHECK: star: * +CHECK: l_paren: ( +CHECK: kw_exclude_file: EXCLUDE_FILE +CHECK: l_paren: ( +CHECK: identifier: *crtend.o +CHECK: identifier: *crtend?.o +CHECK: r_paren: ) +CHECK: identifier: .ctors +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: kw_keep: KEEP +CHECK: l_paren: ( +CHECK: star: * +CHECK: l_paren: ( +CHECK: kw_sort_by_name: SORT +CHECK: l_paren: ( +CHECK: identifier: .ctors.* +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: kw_keep: KEEP +CHECK: l_paren: ( +CHECK: star: * +CHECK: l_paren: ( +CHECK: identifier: .ctors +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: r_brace: } +CHECK: identifier: .dtors +CHECK: colon: : +CHECK: l_brace: { +CHECK: kw_keep: KEEP +CHECK: l_paren: ( +CHECK: identifier: *crtbegin.o +CHECK: l_paren: ( +CHECK: identifier: .dtors +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: kw_keep: KEEP +CHECK: l_paren: ( +CHECK: identifier: *crtbegin?.o +CHECK: l_paren: ( +CHECK: identifier: .dtors +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: kw_keep: KEEP +CHECK: l_paren: ( +CHECK: star: * +CHECK: l_paren: ( +CHECK: kw_exclude_file: EXCLUDE_FILE +CHECK: l_paren: ( +CHECK: identifier: *crtend.o +CHECK: identifier: *crtend?.o +CHECK: r_paren: ) +CHECK: identifier: .dtors +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: kw_keep: KEEP +CHECK: l_paren: ( +CHECK: star: * +CHECK: l_paren: ( +CHECK: kw_sort_by_name: SORT +CHECK: l_paren: ( +CHECK: identifier: .dtors.* +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: kw_keep: KEEP +CHECK: l_paren: ( +CHECK: star: * +CHECK: l_paren: ( +CHECK: identifier: .dtors +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: r_brace: } +CHECK: identifier: . +CHECK: equal: = +CHECK: identifier: DATA_SEGMENT_RELRO_END +CHECK: l_paren: ( +CHECK: identifier: SIZEOF +CHECK: l_paren: ( +CHECK: identifier: .got.plt +CHECK: r_paren: ) +CHECK: greaterequal: >= +CHECK: number: 24 +CHECK: question: ? +CHECK: number: 24 +CHECK: colon: : +CHECK: number: 0 +CHECK: comma: , +CHECK: identifier: . +CHECK: r_paren: ) +CHECK: semicolon: ; +CHECK: identifier: .got.plt +CHECK: colon: : +CHECK: l_brace: { +CHECK: star: * +CHECK: l_paren: ( +CHECK: identifier: .got.plt +CHECK: r_paren: ) +CHECK: star: * +CHECK: l_paren: ( +CHECK: identifier: .igot.plt +CHECK: r_paren: ) +CHECK: r_brace: } +CHECK: identifier: .lrodata +CHECK: kw_align: ALIGN +CHECK: l_paren: ( +CHECK: identifier: CONSTANT +CHECK: l_paren: ( +CHECK: identifier: MAXPAGESIZE +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: plus: + +CHECK: l_paren: ( +CHECK: identifier: . +CHECK: amp: & +CHECK: l_paren: ( +CHECK: identifier: CONSTANT +CHECK: l_paren: ( +CHECK: identifier: MAXPAGESIZE +CHECK: r_paren: ) +CHECK: minus: - +CHECK: number: 1 +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: colon: : +CHECK: l_brace: { +CHECK: star: * +CHECK: l_paren: ( +CHECK: identifier: .lrodata +CHECK: identifier: .lrodata.* +CHECK: identifier: .gnu.linkonce.lr.* +CHECK: r_paren: ) +CHECK: r_brace: } +CHECK: identifier: .ldata +CHECK: kw_align: ALIGN +CHECK: l_paren: ( +CHECK: identifier: CONSTANT +CHECK: l_paren: ( +CHECK: identifier: MAXPAGESIZE +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: plus: + +CHECK: l_paren: ( +CHECK: identifier: . +CHECK: amp: & +CHECK: l_paren: ( +CHECK: identifier: CONSTANT +CHECK: l_paren: ( +CHECK: identifier: MAXPAGESIZE +CHECK: r_paren: ) +CHECK: minus: - +CHECK: number: 1 +CHECK: r_paren: ) +CHECK: r_paren: ) +CHECK: colon: : +CHECK: l_brace: { +CHECK: star: * +CHECK: l_paren: ( +CHECK: identifier: .ldata +CHECK: identifier: .ldata.* +CHECK: identifier: .gnu.linkonce.l.* +CHECK: r_paren: ) +CHECK: identifier: . +CHECK: equal: = +CHECK: kw_align: ALIGN +CHECK: l_paren: ( +CHECK: identifier: . +CHECK: exclaimequal: != +CHECK: number: 0 +CHECK: question: ? +CHECK: number: 64 +CHECK: slash: / +CHECK: number: 8 +CHECK: colon: : +CHECK: number: 1 +CHECK: r_paren: ) +CHECK: semicolon: ; +CHECK: r_brace: } +CHECK: identifier: . +CHECK: equal: = +CHECK: kw_align: ALIGN +CHECK: l_paren: ( +CHECK: number: 64 +CHECK: slash: / +CHECK: number: 8 +CHECK: r_paren: ) +CHECK: semicolon: ; +CHECK: identifier: _end +CHECK: equal: = +CHECK: identifier: . +CHECK: semicolon: ; +CHECK: kw_provide: PROVIDE +CHECK: l_paren: ( +CHECK: identifier: end +CHECK: equal: = +CHECK: identifier: . +CHECK: r_paren: ) +CHECK: semicolon: ; +CHECK: identifier: . +CHECK: equal: = +CHECK: identifier: DATA_SEGMENT_END +CHECK: l_paren: ( +CHECK: identifier: . +CHECK: r_paren: ) +CHECK: semicolon: ; +CHECK: kw_discard: /DISCARD/ +CHECK: colon: : +CHECK: l_brace: { +CHECK: star: * +CHECK: l_paren: ( +CHECK: identifier: .note.GNU-stack +CHECK: r_paren: ) +CHECK: star: * +CHECK: l_paren: ( +CHECK: identifier: .gnu_debuglink +CHECK: r_paren: ) +CHECK: star: * +CHECK: l_paren: ( +CHECK: identifier: .gnu.lto_* +CHECK: r_paren: ) +CHECK: r_brace: } +CHECK: r_brace: } +CHECK: eof: +CHECK: SEARCH_DIR("/usr/x86_64-linux-gnu/lib64") +CHECK: SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu") +CHECK: SECTIONS +CHECK: { +CHECK: PROVIDE(__executable_start = SEGMENT_START(text-segment, 4194304)) +CHECK: . = (SEGMENT_START(text-segment, 4194304) + SIZEOF_HEADERS) +CHECK: .interp : +CHECK: { +CHECK: *(.interp) +CHECK: } +CHECK: .note.gnu.build-id : +CHECK: { +CHECK: *(.note.gnu.build-id) +CHECK: } +CHECK: .hash : +CHECK: { +CHECK: *(.hash) +CHECK: } +CHECK: .rela.dyn : +CHECK: { +CHECK: *(.rela.init) +CHECK: *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) +CHECK: *(.rela.fini) +CHECK: *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) +CHECK: } +CHECK: .rela.plt : +CHECK: { +CHECK: *(.rela.plt) +CHECK: PROVIDE_HIDDEN(__rela_iplt_start = .) +CHECK: *(.rela.iplt) +CHECK: PROVIDE_HIDDEN(__rela_iplt_end = .) +CHECK: } +CHECK: .init : +CHECK: { +CHECK: KEEP(*(SORT_NONE(.init))) +CHECK: } =0x909090909090909090909090 +CHECK: PROVIDE(__etext = .) +CHECK: .eh_frame : +CHECK: ONLY_IF_RO { +CHECK: KEEP(*(.eh_frame)) +CHECK: } +CHECK: .exception_ranges : +CHECK: ONLY_IF_RO { +CHECK: *(.exception_ranges .exception_ranges*) +CHECK: } +CHECK: . = (ALIGN(CONSTANT(MAXPAGESIZE)) - ((CONSTANT(MAXPAGESIZE) - .) & (CONSTANT(MAXPAGESIZE) - 1))) +CHECK: . = DATA_SEGMENT_ALIGN(CONSTANT(MAXPAGESIZE), CONSTANT(COMMONPAGESIZE)) +CHECK: .eh_frame : +CHECK: ONLY_IF_RW { +CHECK: KEEP(*(.eh_frame)) +CHECK: } +CHECK: .ctors : +CHECK: { +CHECK: KEEP(*crtbegin.o(.ctors)) +CHECK: KEEP(*crtbegin?.o(.ctors)) +CHECK: KEEP(*(EXCLUDE_FILE(*crtend.o *crtend?.o ) .ctors)) +CHECK: KEEP(*(SORT_BY_NAME(.ctors.*))) +CHECK: KEEP(*(.ctors)) +CHECK: } +CHECK: .dtors : +CHECK: { +CHECK: KEEP(*crtbegin.o(.dtors)) +CHECK: KEEP(*crtbegin?.o(.dtors)) +CHECK: KEEP(*(EXCLUDE_FILE(*crtend.o *crtend?.o ) .dtors)) +CHECK: KEEP(*(SORT_BY_NAME(.dtors.*))) +CHECK: KEEP(*(.dtors)) +CHECK: } +CHECK: . = DATA_SEGMENT_RELRO_END((SIZEOF(.got.plt) >= 24) ? 24 : 0, .) +CHECK: .got.plt : +CHECK: { +CHECK: *(.got.plt) +CHECK: *(.igot.plt) +CHECK: } +CHECK: .lrodata (ALIGN(CONSTANT(MAXPAGESIZE)) + (. & (CONSTANT(MAXPAGESIZE) - 1))) : +CHECK: { +CHECK: *(.lrodata .lrodata.* .gnu.linkonce.lr.*) +CHECK: } +CHECK: .ldata (ALIGN(CONSTANT(MAXPAGESIZE)) + (. & (CONSTANT(MAXPAGESIZE) - 1))) : +CHECK: { +CHECK: *(.ldata .ldata.* .gnu.linkonce.l.*) +CHECK: . = ALIGN((. != 0) ? (64 / 8) : 1) +CHECK: } +CHECK: . = ALIGN((64 / 8)) +CHECK: _end = . +CHECK: PROVIDE(end = .) +CHECK: . = DATA_SEGMENT_END(.) +CHECK: : +CHECK: { +CHECK: *(.note.GNU-stack) +CHECK: *(.gnu_debuglink) +CHECK: *(.gnu.lto_*) +CHECK: } +CHECK: } +*/ diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000000000..2d45e4fae2a7d --- /dev/null +++ b/test/Makefile @@ -0,0 +1,71 @@ +LLD_LEVEL := .. +include $(LLD_LEVEL)/Makefile + +# Test in all immediate subdirectories if unset. +ifdef TESTSUITE +TESTDIRS := $(TESTSUITE:%=$(PROJ_SRC_DIR)/%) +else +TESTDIRS ?= $(PROJ_SRC_DIR) +endif + +# 'lit' wants objdir paths, so it will pick up the lit.site.cfg. +TESTDIRS := $(TESTDIRS:$(PROJ_SRC_DIR)%=$(PROJ_OBJ_DIR)%) + +# Allow EXTRA_TESTDIRS to provide additional test directories. +TESTDIRS += $(EXTRA_TESTDIRS) + +ifndef TESTARGS +ifdef VERBOSE +TESTARGS = -v +else +TESTARGS = -s -v +endif +endif + +# Make sure any extra test suites can find the main site config. +LIT_ARGS := --param lld_site_config=$(PROJ_OBJ_DIR)/lit.site.cfg + +ifdef VG + LIT_ARGS += "--vg" +endif + +all:: lit.site.cfg Unit/lit.site.cfg + @ echo '--- Running lld tests for $(TARGET_TRIPLE) ---' + @ $(PYTHON) $(LLVM_SRC_ROOT)/utils/lit/lit.py \ + $(LIT_ARGS) $(TESTARGS) $(TESTDIRS) + +FORCE: + +lit.site.cfg: FORCE + @echo "Making lld 'lit.site.cfg' file..." + @$(ECHOPATH) s=@LLVM_SOURCE_DIR@=$(LLVM_SRC_ROOT)=g > lit.tmp + @$(ECHOPATH) s=@LLVM_BINARY_DIR@=$(LLVM_OBJ_ROOT)=g >> lit.tmp + @$(ECHOPATH) s=@LLVM_TOOLS_DIR@=$(ToolDir)=g >> lit.tmp + @$(ECHOPATH) s=@LLVM_LIBS_DIR@=$(LibDir)=g >> lit.tmp + @$(ECHOPATH) s=@LLD_SOURCE_DIR@=$(PROJ_SRC_DIR)/..=g >> lit.tmp + @$(ECHOPATH) s=@LLD_BINARY_DIR@=$(PROJ_OBJ_DIR)/..=g >> lit.tmp + @$(ECHOPATH) s=@TARGET_TRIPLE@=$(TARGET_TRIPLE)=g >> lit.tmp + @sed -f lit.tmp $(PROJ_SRC_DIR)/lit.site.cfg.in > $@ + @-rm -f lit.tmp + +Unit/lit.site.cfg: FORCE + @echo "Making lld 'Unit/lit.site.cfg' file..." + @$(MKDIR) $(dir $@) + @$(ECHOPATH) s=@LLVM_SOURCE_DIR@=$(LLVM_SRC_ROOT)=g > unit.tmp + @$(ECHOPATH) s=@LLVM_BINARY_DIR@=$(LLVM_OBJ_ROOT)=g >> unit.tmp + @$(ECHOPATH) s=@LLVM_TOOLS_DIR@=$(ToolDir)=g >> unit.tmp + @$(ECHOPATH) s=@LLVM_LIBS_DIR@=$(LibDir)=g >> unit.tmp + @$(ECHOPATH) s=@LLD_SOURCE_DIR@=$(PROJ_SRC_DIR)/..=g >> unit.tmp + @$(ECHOPATH) s=@LLD_BINARY_DIR@=$(PROJ_OBJ_DIR)/..=g >> unit.tmp + @$(ECHOPATH) s=@TARGET_TRIPLE@=$(TARGET_TRIPLE)=g >> unit.tmp + @$(ECHOPATH) s=@LLVM_BUILD_MODE@=$(BuildMode)=g >> unit.tmp + @$(ECHOPATH) s=@ENABLE_SHARED@=$(ENABLE_SHARED)=g >> unit.tmp + @$(ECHOPATH) s=@SHLIBDIR@=$(SharedLibDir)=g >> unit.tmp + @$(ECHOPATH) s=@SHLIBPATH_VAR@=$(SHLIBPATH_VAR)=g >> unit.tmp + @sed -f unit.tmp $(PROJ_SRC_DIR)/Unit/lit.site.cfg.in > $@ + @-rm -f unit.tmp + +clean:: + @ find . -name Output | xargs rm -fr + +.PHONY: all report clean diff --git a/test/Unit/lit.cfg b/test/Unit/lit.cfg new file mode 100644 index 0000000000000..4bc973a58edd7 --- /dev/null +++ b/test/Unit/lit.cfg @@ -0,0 +1,23 @@ +# -*- Python -*- + +# Configuration file for the 'lit' test runner. + +import os + +import lit.formats + +# name: The name of this test suite. +config.name = 'lld-Unit' + +# suffixes: A list of file extensions to treat as test files. +config.suffixes = [] + +# test_source_root: The root path where unit test binaries are located. +# test_exec_root: The root path where tests should be run. +config.test_source_root = os.path.join(config.lld_obj_root, 'unittests') +config.test_exec_root = config.test_source_root + +# testFormat: The test format to use to interpret tests. +if not hasattr(config, 'llvm_build_mode'): + lit_config.fatal("unable to find llvm_build_mode value on config") +config.test_format = lit.formats.GoogleTest(config.llvm_build_mode, 'Tests') diff --git a/test/Unit/lit.site.cfg.in b/test/Unit/lit.site.cfg.in new file mode 100644 index 0000000000000..74c5eca91153c --- /dev/null +++ b/test/Unit/lit.site.cfg.in @@ -0,0 +1,25 @@ +## Autogenerated by LLVM/lld configuration. +# Do not edit! +config.llvm_src_root = "@LLVM_SOURCE_DIR@" +config.llvm_obj_root = "@LLVM_BINARY_DIR@" +config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" +config.llvm_libs_dir = "@LLVM_LIBS_DIR@" +config.llvm_build_mode = "@LLVM_BUILD_MODE@" +config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@" +config.lld_obj_root = "@LLD_BINARY_DIR@" +config.lld_src_root = "@LLD_SOURCE_DIR@" +config.target_triple = "@TARGET_TRIPLE@" +config.python_executable = "@PYTHON_EXECUTABLE@" + +# Support substitution of the tools and libs dirs with user parameters. This is +# used when we can't determine the tool dir at configuration time. +try: + config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params + config.llvm_libs_dir = config.llvm_libs_dir % lit_config.params + config.llvm_build_mode = config.llvm_build_mode % lit_config.params +except KeyError as e: + key, = e.args + lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key)) + +# Let the main config do the real work. +lit_config.load_config(config, "@LLD_SOURCE_DIR@/test/Unit/lit.cfg") diff --git a/test/core/absolute-basic.objtxt b/test/core/absolute-basic.objtxt new file mode 100644 index 0000000000000..edfbe8629b0df --- /dev/null +++ b/test/core/absolute-basic.objtxt @@ -0,0 +1,23 @@ +# RUN: lld -core --dead-strip %s | FileCheck %s + +# +# Test that absolute symbols are parsed and preserved +# + +--- +absolute-atoms: + - name: putchar + value: 0xFFFF0040 + + - name: reset + value: 0xFFFF0080 + +... + + +# CHECK: absolute-atoms: +# CHECK: name: putchar +# CHECK: value: 0x00000000FFFF0040 +# CHECK: name: reset +# CHECK: value: 0x00000000FFFF0080 +# CHECK: ... diff --git a/test/core/absolute-local.objtxt b/test/core/absolute-local.objtxt new file mode 100644 index 0000000000000..1ba4c7f04326e --- /dev/null +++ b/test/core/absolute-local.objtxt @@ -0,0 +1,25 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that absolute symbols with local scope do not cause name conflict +# +--- +absolute-atoms: + - name: putchar + ref-name: pc1 + value: 0xFFFF0040 + scope: static + + - name: putchar + ref-name: pc2 + value: 0xFFFF0040 + scope: static +... + +# CHECK: --- +# CHECK: absolute-atoms: +# CHECK: - name: putchar +# CHECK: value: 0x00000000FFFF0040 +# CHECK: - name: putchar +# CHECK: value: 0x00000000FFFF0040 +# CHECK: ... diff --git a/test/core/archive-basic.objtxt b/test/core/archive-basic.objtxt new file mode 100644 index 0000000000000..ec825c1a7e525 --- /dev/null +++ b/test/core/archive-basic.objtxt @@ -0,0 +1,44 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Tests archives in YAML. Tests that an undefined in a regular file will load +# all atoms in select archive members. +# + +--- !native +defined-atoms: + - name: foo + type: code + +undefined-atoms: + - name: bar + +--- !archive +members: + - name: bar.o + content: !native + defined-atoms: + - name: bar + scope: global + type: code + + - name: bar2 + type: code + + - name: baz.o + content: !native + defined-atoms: + - name: baz + scope: global + type: code + + - name: baz2 + type: code +... + +# CHECK: name: foo +# CHECK-NOT: undefined-atoms: +# CHECK: name: bar +# CHECK: name: bar2 +# CHECK-NOT: name: baz +# CHECK: ... diff --git a/test/core/archive-chain.objtxt b/test/core/archive-chain.objtxt new file mode 100644 index 0000000000000..0f80985ec112a --- /dev/null +++ b/test/core/archive-chain.objtxt @@ -0,0 +1,70 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Tests that an undefine in one archive can force a load from another archive. +# + +--- !native +defined-atoms: + - name: foo + type: code + +undefined-atoms: + - name: bar1 + +--- !archive +members: + - name: bar1.o + content: !native + defined-atoms: + - name: bar1 + scope: global + type: code + + - name: bar1b + type: code + + undefined-atoms: + - name: baz1 + + - name: bar2.o + content: !native + defined-atoms: + - name: bar2 + scope: global + type: code + + - name: bar2b + type: code + +--- !archive +members: + - name: baz1.o + content: !native + defined-atoms: + - name: baz1 + scope: global + type: code + + - name: baz1b + type: code + + - name: baz2.o + content: !native + defined-atoms: + - name: baz2 + scope: global + type: code + + - name: baz2b + type: code +... + +# CHECK: name: foo +# CHECK: name: bar1 +# CHECK: name: bar1b +# CHECK-NOT: name: bar2 +# CHECK: name: baz1 +# CHECK: name: baz1b +# CHECK-NOT: name: baz2 +# CHECK: ... diff --git a/test/core/archive-tentdef-search.objtxt b/test/core/archive-tentdef-search.objtxt new file mode 100644 index 0000000000000..3d26778e4986f --- /dev/null +++ b/test/core/archive-tentdef-search.objtxt @@ -0,0 +1,42 @@ +# RUN: lld -core %s | FileCheck -check-prefix=CHK1 %s +# RUN: lld -core --commons-search-archives %s | FileCheck -check-prefix=CHK2 %s + +# +# Tests that -commons-search-archives cause core linker to look for overrides +# of tentative definition in archives, and that not using that option +# does not search. +# + +--- !native +defined-atoms: + - name: foo + type: code + + - name: bar + scope: global + type: zero-fill + merge: as-tentative + +--- !archive +members: + - name: bar.o + content: !native + defined-atoms: + - name: bar + scope: global + type: data + + - name: bar2 + type: data +... + +# CHK1: name: foo +# CHK1: name: bar +# CHK1: merge: as-tentative +# CHK1: ... + +# CHK2: name: foo +# CHK2: name: bar +# CHK2-NOT: merge: as-tentative +# CHK2: name: bar2 +# CHK2: ... diff --git a/test/core/associates.objtxt b/test/core/associates.objtxt new file mode 100644 index 0000000000000..bf780693ab709 --- /dev/null +++ b/test/core/associates.objtxt @@ -0,0 +1,30 @@ +# RUN: lld -core %s | FileCheck %s + +--- +defined-atoms: + - name: f1 + merge: as-weak + scope: global + references: + - kind: associate + target: f2 + - name: f2 +--- +defined-atoms: + - name: f1 + merge: as-weak + scope: global + references: + - kind: associate + target: f2 + - name: f2 +... + +# CHECK: defined-atoms: +# CHECK: - name: f1 +# CHECK: scope: global +# CHECK: references: +# CHECK: - kind: associate +# CHECK: target: f2 +# CHECK: - name: f2 +# CHECK-NOT: - name: f2 diff --git a/test/core/auto-hide-coalesce.objtxt b/test/core/auto-hide-coalesce.objtxt new file mode 100644 index 0000000000000..ad82d5afc5738 --- /dev/null +++ b/test/core/auto-hide-coalesce.objtxt @@ -0,0 +1,60 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Tests auto-hide bit during coalescing +# + +--- +defined-atoms: + - name: _inlineFunc1 + scope: global + type: code + merge: as-weak + + - name: _inlineFunc2 + scope: global + type: code + merge: as-weak + + - name: _inlineFunc3 + scope: global + type: code + merge: as-addressed-weak + + - name: _inlineFunc4 + scope: global + type: code + merge: as-addressed-weak +--- +defined-atoms: + - name: _inlineFunc1 + scope: global + type: code + merge: as-weak + + - name: _inlineFunc2 + scope: global + type: code + merge: as-addressed-weak + + - name: _inlineFunc3 + scope: global + type: code + merge: as-weak + + - name: _inlineFunc4 + scope: global + type: code + merge: as-addressed-weak +... + + +# CHECK: name: _inlineFunc1 +# CHECK: merge: as-weak +# CHECK: name: _inlineFunc3 +# CHECK: merge: as-addressed-weak +# CHECK: name: _inlineFunc4 +# CHECK: merge: as-addressed-weak +# CHECK: name: _inlineFunc2 +# CHECK: merge: as-addressed-weak +# CHECK: ... diff --git a/test/core/code-model-attributes.objtxt b/test/core/code-model-attributes.objtxt new file mode 100644 index 0000000000000..8c30e868567d0 --- /dev/null +++ b/test/core/code-model-attributes.objtxt @@ -0,0 +1,50 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that code model attributes are preserved +# + +--- +defined-atoms: + - name: _def +--- +defined-atoms: + - name: _none + code-model: none +--- +defined-atoms: + - name: _mips_pic + code-model: mips-pic +--- +defined-atoms: + - name: _mips_micro + code-model: mips-micro +--- +defined-atoms: + - name: _mips_micro_pic + code-model: mips-micro-pic +--- +defined-atoms: + - name: _mips_16 + code-model: mips-16 +... + +# CHECK: name: _def +# CHECK-NOT: code-model: mips-pic +# CHECK-NOT: code-model: mips-micro +# CHECK-NOT: code-model: mips-micro-pic +# CHECK-NOT: code-model: mips-16 +# CHECK: name: _none +# CHECK-NOT: code-model: mips-pic +# CHECK-NOT: code-model: mips-micro +# CHECK-NOT: code-model: mips-micro-pic +# CHECK-NOT: code-model: mips-16 +# CHECK: name: _mips_pic +# CHECK: code-model: mips-pic +# CHECK: name: _mips_micro +# CHECK: code-model: mips-micro +# CHECK: name: _mips_micro_pic +# CHECK: code-model: mips-micro-pic +# CHECK: name: _mips_16 +# CHECK: code-model: mips-16 +# CHECK: ... diff --git a/test/core/constants-coalesce.objtxt b/test/core/constants-coalesce.objtxt new file mode 100644 index 0000000000000..a82f680090874 --- /dev/null +++ b/test/core/constants-coalesce.objtxt @@ -0,0 +1,60 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that duplicate merge-by-content anonymous constants are coalesced +# and non-mergable duplicate constants are not coalesced. +# + +--- +defined-atoms: + - ref-name: L4-byte + type: constant + merge: by-content + content: [ 01, 02, 03, 04 ] + + - ref-name: L8-byte + type: constant + merge: by-content + content: [ 01, 23, 45, 67, 89, AB, CD, EF ] + + - ref-name: L1 + type: constant + content: [ 01, 02 ] +--- +defined-atoms: + - ref-name: L1 + type: constant + content: [ 01, 02 ] + - ref-name: L2 + type: constant + merge: by-content + content: [ 01, 02, 03, 04 ] +--- +defined-atoms: + - ref-name: L2 + type: constant + merge: by-content + content: [ 01, 23, 45, 67, 89, AB, CD, EF ] + - ref-name: L3 + type: constant + merge: by-content + content: [ 01, 02, 03 ] +... + +# CHECK-NOT: name: +# CHECK: type: constant +# CHECK: content: [ 01, 02, 03, 04 ] +# CHECK: merge: by-content +# CHECK: type: constant +# CHECK: content: [ 01, 23, 45, 67, 89, AB, CD, EF ] +# CHECK: merge: by-content +# CHECK: type: constant +# CHECK: content: [ 01, 02 ] +# CHECK: type: constant +# CHECK: content: [ 01, 02 ] +# CHECK: type: constant +# CHECK: content: [ 01, 02, 03 ] +# CHECK: merge: by-content +# CHECK: ... + + diff --git a/test/core/cstring-coalesce.objtxt b/test/core/cstring-coalesce.objtxt new file mode 100644 index 0000000000000..78986a08c6400 --- /dev/null +++ b/test/core/cstring-coalesce.objtxt @@ -0,0 +1,41 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that duplicate c-strings are coalesced +# + +--- +defined-atoms: + - ref-name: L0 + type: c-string + merge: by-content + content: [ 68, 65, 6c, 6c, 6f, 00 ] + + - ref-name: L1 + type: c-string + merge: by-content + content: [ 74, 68, 65, 72, 65, 00 ] +--- +defined-atoms: + - ref-name: L2 + type: c-string + merge: by-content + content: [ 68, 65, 6c, 6c, 6f, 00 ] +--- +defined-atoms: + - ref-name: L2 + type: c-string + merge: by-content + content: [ 74, 68, 65, 72, 65, 00 ] +... + +# CHECK-NOT: name: +# CHECK: type: c-string +# CHECK: content: [ 68, 65, 6C, 6C, 6F, 00 ] +# CHECK: merge: by-content +# CHECK: type: c-string +# CHECK: content: [ 74, 68, 65, 72, 65, 00 ] +# CHECK: merge: by-content +# CHECK: ... + + diff --git a/test/core/custom-section-coalesce.objtxt b/test/core/custom-section-coalesce.objtxt new file mode 100644 index 0000000000000..e9bada56938d9 --- /dev/null +++ b/test/core/custom-section-coalesce.objtxt @@ -0,0 +1,78 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that custom sections are preserved when duplicate merge-by-content +# constants are coalesced. +# + +--- +defined-atoms: + - ref-name: L1 + type: constant + merge: by-content + content: [ 01, 02, 03, 04 ] + section-choice: custom-required + section-name: .mysection + + - ref-name: L2 + type: constant + merge: by-content + content: [ 05, 06, 07, 08 ] + section-choice: custom-required + section-name: .mysection + + - ref-name: L3 + type: constant + merge: by-content + content: [ 01, 02, 03, 04 ] + +--- +defined-atoms: + - ref-name: L1 + type: constant + merge: by-content + content: [ 01, 02, 03, 04 ] + section-choice: custom-required + section-name: .mysection + + - ref-name: L2 + type: constant + merge: by-content + content: [ 01, 02, 03, 04 ] + section-choice: custom-required + section-name: .mysection2 +--- +defined-atoms: + - ref-name: L1 + type: constant + merge: by-content + content: [ 05, 06, 07, 08 ] + section-choice: custom-required + section-name: .mysection + + - ref-name: L2 + type: constant + merge: by-content + content: [ 01, 02, 03, 04 ] +... + + +# CHECK:defined-atoms: +# CHECK: - type: constant +# CHECK: content: [ 01, 02, 03, 04 ] +# CHECK: merge: by-content +# CHECK: section-choice: custom-required +# CHECK: section-name: .mysection +# CHECK: - type: constant +# CHECK: content: [ 05, 06, 07, 08 ] +# CHECK: merge: by-content +# CHECK: section-choice: custom-required +# CHECK: section-name: .mysection +# CHECK: - type: constant +# CHECK: content: [ 01, 02, 03, 04 ] +# CHECK: merge: by-content +# CHECK: - type: constant +# CHECK: content: [ 01, 02, 03, 04 ] +# CHECK: merge: by-content +# CHECK: section-choice: custom-required +# CHECK: section-name: .mysection2 diff --git a/test/core/custom-section.objtxt b/test/core/custom-section.objtxt new file mode 100644 index 0000000000000..ce305e9af38e1 --- /dev/null +++ b/test/core/custom-section.objtxt @@ -0,0 +1,34 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that custom sections are preserved +# + +--- +defined-atoms: + - name: _foo1 + scope: global + section-choice: content + + - name: _foo2 + scope: global + section-choice: custom + section-name: __foozle + + - name: _foo3 + scope: global + section-choice: custom-required + section-name: __boozle + +... + + +# CHECK: name: _foo1 +# CHECK-NOT: section-name: +# CHECK: name: _foo2 +# CHECK: section-choice: custom +# CHECK: section-name: __foozle +# CHECK: name: _foo3 +# CHECK: section-choice: custom-required +# CHECK: section-name: __boozle +# CHECK: ... diff --git a/test/core/dead-strip-attributes.objtxt b/test/core/dead-strip-attributes.objtxt new file mode 100644 index 0000000000000..dcb35a21e2616 --- /dev/null +++ b/test/core/dead-strip-attributes.objtxt @@ -0,0 +1,29 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that dead strip attributes are preserved +# + +--- +defined-atoms: + - name: _foo1 + dead-strip: normal +--- +defined-atoms: + - name: _foo2 + dead-strip: never +--- +defined-atoms: + - name: _foo3 + dead-strip: always +... + + +# CHECK: name: _foo1 +# CHECK-NOT: dead-strip: never +# CHECK-NOT: dead-strip: always +# CHECK: name: _foo2 +# CHECK: dead-strip: never +# CHECK: name: _foo3 +# CHECK: dead-strip: always +# CHECK: ... diff --git a/test/core/dead-strip-basic.objtxt b/test/core/dead-strip-basic.objtxt new file mode 100644 index 0000000000000..64cd2291c76b8 --- /dev/null +++ b/test/core/dead-strip-basic.objtxt @@ -0,0 +1,62 @@ +# RUN: lld -core --dead-strip %s | FileCheck -check-prefix=CHK1 %s +# RUN: lld -core %s | FileCheck -check-prefix=CHK2 %s + +# +# Test that -dead-strip removes unreachable code and data +# and that not using that option leaves them. +# + +--- +defined-atoms: + - name: entry + dead-strip: never + references: + - offset: 1 + kind: pcrel32 + target: bar + - offset: 6 + kind: pcrel32 + target: baz + + - name: mydead1 + scope: global + +undefined-atoms: + - name: bar + + - name: baz +--- +defined-atoms: + - name: mydead2 + scope: global + type: data + + - name: bar + scope: global + type: data +--- +defined-atoms: + - name: baz + scope: global + type: code + + - name: mydead3 + type: code +... + + +# CHK1: name: entry +# CHK1-NOT: name: mydead1 +# CHK1: name: bar +# CHK1-NOT: name: mydead2 +# CHK1: name: baz +# CHK1-NOT: name: mydead3 +# CHK1: ... + +# CHK2: name: entry +# CHK2: name: mydead1 +# CHK2: name: mydead2 +# CHK2: name: bar +# CHK2: name: baz +# CHK2: name: mydead3 +# CHK2: ... diff --git a/test/core/dead-strip-globals.objtxt b/test/core/dead-strip-globals.objtxt new file mode 100644 index 0000000000000..8feb235d07c3c --- /dev/null +++ b/test/core/dead-strip-globals.objtxt @@ -0,0 +1,60 @@ +# RUN: lld -core --dead-strip --keep-globals %s | FileCheck -check-prefix=CHK1 %s +# RUN: lld -core --dead-strip %s | FileCheck -check-prefix=CHK2 %s + +# +# Test that -keep-globals prevents -dead-strip from removing globals. +# + +--- +defined-atoms: + - name: entry + dead-strip: never + references: + - offset: 1 + kind: pcrel32 + target: bar + - offset: 6 + kind: pcrel32 + target: baz + + - name: myglobal1 + scope: global + +undefined-atoms: + - name: bar + - name: baz +--- +defined-atoms: + - name: myglobal2 + scope: global + type: data + + - name: bar + scope: hidden + type: data +--- +defined-atoms: + - name: baz + scope: hidden + type: code + + - name: mydead + type: code +... + + +# CHK1: name: entry +# CHK1: name: myglobal1 +# CHK1: name: myglobal2 +# CHK1: name: bar +# CHK1: name: baz +# CHK1-NOT: name: mydead +# CHK1: ... + +# CHK2: name: entry +# CHK2-NOT: name: myglobal1 +# CHK2-NOT: name: myglobal2 +# CHK2: name: bar +# CHK2: name: baz +# CHK2-NOT: name: mydead +# CHK2: ... diff --git a/test/core/dead-strip-reverse.objtxt b/test/core/dead-strip-reverse.objtxt new file mode 100644 index 0000000000000..f471bebcdf4e7 --- /dev/null +++ b/test/core/dead-strip-reverse.objtxt @@ -0,0 +1,25 @@ +# RUN: lld -core --dead-strip %s | FileCheck -check-prefix=CHECK1 %s +# RUN: lld -core %s | FileCheck -check-prefix=CHECK2 %s + +--- +defined-atoms: + - name: entry + dead-strip: never + scope: global + references: + - kind: layout-after + offset: 0 + target: def + - name: def + scope: global + - name: dead + scope: global +... + +# CHECK1: name: entry +# CHECK1: name: def +# CHECK1-NOT: name: dead + +# CHECK2: name: entry +# CHECK2: name: def +# CHECK2: name: dead diff --git a/test/core/error-atom-attribute.objtxt b/test/core/error-atom-attribute.objtxt new file mode 100644 index 0000000000000..6643aba29eea3 --- /dev/null +++ b/test/core/error-atom-attribute.objtxt @@ -0,0 +1,19 @@ +# RUN: not lld -core %s 2> %t.err +# RUN: FileCheck %s < %t.err + +# +# Test that unknown atom attribute produces a readable error. +# + +--- +defined-atoms: + - name: entry + scope: hidden + foobar: true + dead-strip: never + +... + + +# CHECK: error: unknown key 'foobar' +# CHECK: foobar diff --git a/test/core/error-atom-content-byte-value.objtxt b/test/core/error-atom-content-byte-value.objtxt new file mode 100644 index 0000000000000..6e675576461a5 --- /dev/null +++ b/test/core/error-atom-content-byte-value.objtxt @@ -0,0 +1,18 @@ +# RUN: not lld -core %s 2> %t.err +# RUN: FileCheck %s < %t.err + +# +# Test that an invalid hex byte produces a readable error. +# + +--- +defined-atoms: + - name: entry + scope: hidden + content: [ A5, 00, 4G, 1F ] + +... + + +# CHECK: error: invalid two-digit-hex number +# CHECK: 4G diff --git a/test/core/error-atom-content-bytes.objtxt b/test/core/error-atom-content-bytes.objtxt new file mode 100644 index 0000000000000..a8a82b2b45e19 --- /dev/null +++ b/test/core/error-atom-content-bytes.objtxt @@ -0,0 +1,19 @@ +# RUN: not lld -core %s 2> %t.err +# RUN: FileCheck %s < %t.err + +# +# Test that an out of range byte value produces a readable error. +# + +--- +defined-atoms: + - name: entry + scope: hidden + content: [ A5, 1234, 00, 4F ] + +... + + +# CHECK: error: out of range two-digit-hex number +# CHECK: 1234 + diff --git a/test/core/error-atom-type.objtxt b/test/core/error-atom-type.objtxt new file mode 100644 index 0000000000000..b0943f8e27492 --- /dev/null +++ b/test/core/error-atom-type.objtxt @@ -0,0 +1,19 @@ +# RUN: not lld -core %s 2> %t.err +# RUN: FileCheck %s < %t.err + +# +# Test that an unknown content type produces a readable error. +# + +--- +defined-atoms: + - name: entry + scope: hidden + type: superluminal + dead-strip: never + +... + + +# CHECK: error: unknown enumerated scalar +# CHECK: superluminal diff --git a/test/core/error-atom-undefined-wrong-attribue.objtxt b/test/core/error-atom-undefined-wrong-attribue.objtxt new file mode 100644 index 0000000000000..5cdd8519c804e --- /dev/null +++ b/test/core/error-atom-undefined-wrong-attribue.objtxt @@ -0,0 +1,17 @@ +# RUN: not lld -core %s 2> %t.err +# RUN: FileCheck %s < %t.err + +# +# Test that a defined attribute on an undefined atom produces a readable error. +# + +--- +undefined-atoms: + - name: foo + type: code + +... + + +# CHECK: error: unknown key 'type' + diff --git a/test/core/error-duplicate-absolutes.objtxt b/test/core/error-duplicate-absolutes.objtxt new file mode 100644 index 0000000000000..533297e59dcd3 --- /dev/null +++ b/test/core/error-duplicate-absolutes.objtxt @@ -0,0 +1,24 @@ +# RUN: not lld -core %s 2> %t.err +# RUN: FileCheck %s < %t.err + +# +# Test that duplicate absolute atoms produces a readable error. +# + +--- +absolute-atoms: + - name: absatom + value: 0 + scope: global +undefined-atoms: + - name: undefatom +--- +absolute-atoms: + - name: absatom + value: 0 + scope: global +... + + +# CHECK: SymbolTable: error while merging absatom +# CHECK: LLVM ERROR: duplicate symbol error diff --git a/test/core/error-file-attribute.objtxt b/test/core/error-file-attribute.objtxt new file mode 100644 index 0000000000000..d8393dc5e400f --- /dev/null +++ b/test/core/error-file-attribute.objtxt @@ -0,0 +1,17 @@ +# RUN: not lld -core %s 2> %t.err +# RUN: FileCheck %s < %t.err + +# +# Test that unknown file attribute produces a readable error. +# + +--- +aardvark: true +defined-atoms: + - name: entry + scope: hidden + +... + + +# CHECK: error: unknown key 'aardvark' diff --git a/test/core/error-fixup-attribute.objtxt b/test/core/error-fixup-attribute.objtxt new file mode 100644 index 0000000000000..025783a044274 --- /dev/null +++ b/test/core/error-fixup-attribute.objtxt @@ -0,0 +1,21 @@ +# RUN: not lld -core %s 2> %t.err +# RUN: FileCheck %s < %t.err + +# +# Test that unknown fixup attribute produces a readable error. +# + +--- +defined-atoms: + - name: entry + scope: hidden + references: + - offset: 3 + kind: pcrel32 + weasel: bar + addend: 100 + +... + + +# CHECK: error: unknown key 'weasel' diff --git a/test/core/error-fixup-target.objtxt b/test/core/error-fixup-target.objtxt new file mode 100644 index 0000000000000..0e20d1636f50b --- /dev/null +++ b/test/core/error-fixup-target.objtxt @@ -0,0 +1,26 @@ +# RUN: not lld -core %s 2> %t.err +# RUN: FileCheck %s < %t.err + +# +# Test that unbindable target name produces a readable error. +# + +--- +defined-atoms: + - name: entry + scope: hidden + references: + - offset: 3 + kind: pcrel32 + target: bar + - offset: 5 + kind: pcrel32 + target: baz + +undefined-atoms: + - name: bar + +... + + +# CHECK: error: no such atom name: baz diff --git a/test/core/fixups-addend.objtxt b/test/core/fixups-addend.objtxt new file mode 100644 index 0000000000000..d976150459c93 --- /dev/null +++ b/test/core/fixups-addend.objtxt @@ -0,0 +1,50 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test addends in references +# + +--- +defined-atoms: + - name: foo + type: code + content: [ 48, 8D, 3D, 00, 00, 00, 00, + 48, 8D, 3D, 00, 00, 00, 00 ] + references: + - offset: 3 + kind: pcrel32 + target: bar + addend: 100 + - offset: 10 + kind: pcrel32 + target: bar + addend: -50 + + - name: func + type: code + content: [ 48, 8D, 3D, 00, 00, 00, 00, + 48, 8D, 3D, 00, 00, 00, 00 ] + references: + - offset: 3 + kind: pcrel32 + target: bar + addend: 8000000000 + - offset: 10 + kind: pcrel32 + target: bar + addend: -50 + +undefined-atoms: + - name: bar + + +... + +# CHECK: name: foo +# CHECK: references: +# CHECK: addend: 100 +# CHECK: addend: -50 +# CHECK: name: func +# CHECK: references: +# CHECK: addend: 8000000000 +# CHECK: addend: -50 diff --git a/test/core/fixups-dup-named.objtxt b/test/core/fixups-dup-named.objtxt new file mode 100644 index 0000000000000..1c57cd73bf098 --- /dev/null +++ b/test/core/fixups-dup-named.objtxt @@ -0,0 +1,31 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test references referencing multiple atoms that have the same name +# + +--- +defined-atoms: + - name: foo + type: code + content: [ E8, 00, 00, 00, 00, E8, 00, 00, 00, 00 ] + references: + - offset: 1 + kind: pcrel32 + target: bar_1 + - offset: 6 + kind: pcrel32 + target: bar_2 + + - name: bar + ref-name: bar_1 + scope: static + + - name: bar + ref-name: bar_2 + scope: static + + +... + +# CHECK: ... diff --git a/test/core/fixups-named.objtxt b/test/core/fixups-named.objtxt new file mode 100644 index 0000000000000..1427a9b705d15 --- /dev/null +++ b/test/core/fixups-named.objtxt @@ -0,0 +1,36 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test references to simple named atoms +# + +--- +defined-atoms: + - name: foo + type: code + content: [ E8, 00, 00, 00, 00, + E8, 00, 00, 00, 00 ] + references: + - offset: 1 + kind: pcrel32 + target: bar + - offset: 6 + kind: pcrel32 + target: baz + + - name: baz + scope: static + type: code + +undefined-atoms: + - name: bar + + +... + +# CHECK: name: foo +# CHECK: references: +# CHECK: target: bar +# CHECK: target: baz +# CHECK: ... + diff --git a/test/core/fixups-unnamed.objtxt b/test/core/fixups-unnamed.objtxt new file mode 100644 index 0000000000000..88afb6a447a2f --- /dev/null +++ b/test/core/fixups-unnamed.objtxt @@ -0,0 +1,40 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test references to unnamed atoms +# + +--- +defined-atoms: + - name: foo + type: code + content: [ 48, 8D, 3D, 00, 00, 00, 00, + 48, 8D, 3D, 00, 00, 00, 00 ] + references: + - offset: 3 + kind: pcrel32 + target: LC1 + - offset: 10 + kind: pcrel32 + target: LC2 + + + - ref-name: LC1 + type: c-string + merge: by-content + content: [ 68, 65, 6c, 6c, 6f, 00 ] + + - ref-name: LC2 + type: c-string + merge: by-content + content: [ 74, 68, 65, 72, 65, 00 ] + + +... + +# CHECK: name: foo +# CHECK: references: +# CHECK: offset: 3 +# CHECK: offset: 10 +# CHECK: ref-name: +# CHECK: ref-name: diff --git a/test/core/gnulinkonce-rearrange-resolve.objtxt b/test/core/gnulinkonce-rearrange-resolve.objtxt new file mode 100644 index 0000000000000..2a6386a6935dd --- /dev/null +++ b/test/core/gnulinkonce-rearrange-resolve.objtxt @@ -0,0 +1,79 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that gnu linkonce sections are parsed and the first section selected for symbol +# resolution +# + +--- +defined-atoms: + - name: g1 + scope: global + type: gnu-linkonce + references: + - kind: group-child + target: f1 + - kind: group-child + target: f2 + - kind: group-child + target: g1 + - kind: group-child + target: d1 + - name: f1 + scope: global + type: code + - name: f2 + scope: global + type: code + - name: g1 + scope: global + type: code + - name: d1 + scope: global + type: data +--- +defined-atoms: + - name: g1 + scope: global + type: gnu-linkonce + references: + - kind: group-child + target: f1 + - kind: group-child + target: f2 + - kind: group-child + target: g1 + - kind: group-child + target: d1 + - name: f1 + scope: global + type: code + - name: f2 + scope: global + type: code + - name: g1 + scope: global + type: code + - name: d1 + scope: global + type: data +... + +# CHECK: defined-atoms: +# CHECK: - name: g1 +# CHECK: ref-name: [[PARENT:[a-zA-Z\.0-9_]+]] +# CHECK: type: gnu-linkonce +# CHECK: references: +# CHECK: - kind: group-child +# CHECK: target: f1 +# CHECK: - kind: group-child +# CHECK: target: f2 +# CHECK: - kind: group-child +# CHECK: target: [[CHILD:[a-zA-Z\.0-9_]+]] +# CHECK: - kind: group-child +# CHECK: target: d1 +# CHECK: - name: f1 +# CHECK: - name: f2 +# CHECK: - name: g1 +# CHECK: ref-name: [[CHILD]] +# CHECK: - name: d1 diff --git a/test/core/gnulinkonce-remaining-undef.objtxt b/test/core/gnulinkonce-remaining-undef.objtxt new file mode 100644 index 0000000000000..490608209ba98 --- /dev/null +++ b/test/core/gnulinkonce-remaining-undef.objtxt @@ -0,0 +1,80 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that gnu linkonce sections are parsed and the first section selected for +# symbol resolution. The second file which has the same gnu linkonce section has +# a unresolved undefined symbol. lets make sure that the symbol is kept around +# in the final link and remains undefined. +# + +--- +defined-atoms: + - name: f1 + scope: global + type: code + - name: f2 + scope: global + type: code + - name: g1 + scope: global + type: code + - name: d1 + scope: global + type: data + - name: g1 + scope: global + type: gnu-linkonce + references: + - kind: group-child + target: f1 + - kind: group-child + target: f2 + - kind: group-child + target: g1 + - kind: group-child + target: d1 +--- +defined-atoms: + - name: anotherfunction + scope: global + type: data + - name: f1 + scope: global + type: code + - name: f2 + scope: global + type: code + - name: f3 + scope: global + type: code + - name: g1 + scope: global + type: code + - name: d1 + scope: global + type: data + - name: g1 + scope: global + type: gnu-linkonce + references: + - kind: group-child + target: f1 + - kind: group-child + target: f2 + - kind: group-child + target: f3 + - kind: group-child + target: g1 + - kind: group-child + target: d1 +--- +undefined-atoms: + - name: f3 + can-be-null: never +... + +#CHECK: - name: anotherfunction +#CHECK: scope: global +#CHECK: type: data +#CHECK: undefined-atoms: +#CHECK: - name: f3 diff --git a/test/core/gnulinkonce-resolve.objtxt b/test/core/gnulinkonce-resolve.objtxt new file mode 100644 index 0000000000000..817e9cfdd4e06 --- /dev/null +++ b/test/core/gnulinkonce-resolve.objtxt @@ -0,0 +1,89 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that gnu linkonce sections are parsed and the first section selected for symbol +# resolution +# + +--- +defined-atoms: + - name: f1 + scope: global + type: code + - name: f2 + scope: global + type: code + - name: g1 + scope: global + type: code + - name: d1 + scope: global + type: data + - name: g1 + scope: global + type: gnu-linkonce + references: + - kind: group-child + target: f1 + - kind: group-child + target: f2 + - kind: group-child + target: g1 + - kind: group-child + target: d1 +--- +defined-atoms: + - name: f1 + scope: global + type: code + - name: f2 + scope: global + type: code + - name: g1 + scope: global + type: code + - name: d1 + scope: global + type: data + - name: g1 + scope: global + type: gnu-linkonce + references: + - kind: group-child + target: f1 + - kind: group-child + target: f2 + - kind: group-child + target: g1 + - kind: group-child + target: d1 +... + +#CHECK: defined-atoms: +#CHECK: - name: g1 +#CHECK: ref-name: [[PARENT:[a-zA-Z\.0-9_]+]] +#CHECK: scope: global +#CHECK: type: gnu-linkonce +#CHECK: references: +#CHECK: - kind: group-child +#CHECK: offset: 0 +#CHECK: target: f1 +#CHECK: - kind: group-child +#CHECK: offset: 0 +#CHECK: target: f2 +#CHECK: - kind: group-child +#CHECK: offset: 0 +#CHECK: target: [[GCHILD:[a-zA-Z\.0-9_]+]] +#CHECK: - kind: group-child +#CHECK: offset: 0 +#CHECK: target: d1 +#CHECK: - name: f1 +#CHECK: scope: global +#CHECK: - name: f2 +#CHECK: scope: global +#CHECK: - name: g1 +#CHECK: ref-name: [[GCHILD]] +#CHECK: scope: global +#CHECK: - name: d1 +#CHECK: scope: global +#CHECK: type: data diff --git a/test/core/gnulinkonce-simple.objtxt b/test/core/gnulinkonce-simple.objtxt new file mode 100644 index 0000000000000..da325d48c3669 --- /dev/null +++ b/test/core/gnulinkonce-simple.objtxt @@ -0,0 +1,80 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that gnu linkonce sections are parsed properly when there is a reference to a +# atom from outside the gnu linkonce section. +# + +--- +defined-atoms: + - name: f1 + scope: global + type: code + references: + - kind: layout-after + target: anotherfunction + - name: f2 + scope: global + type: code + - name: g1 + scope: global + type: code + - name: d1 + scope: global + type: data + - name: g1 + scope: global + type: gnu-linkonce + references: + - kind: group-child + target: f1 + - kind: group-child + target: f2 + - kind: group-child + target: d1 + - kind: group-child + target: g1 + - name: anotherfunction + scope: global + type: data +--- +undefined-atoms: + - name: f1 + can-be-null: never +... + +#CHECK: defined-atoms: +#CHECK: - name: g1 +#CHECK: ref-name: [[PARENT:[a-zA-Z\.0-9_]+]] +#CHECK: scope: global +#CHECK: type: gnu-linkonce +#CHECK: references: +#CHECK: - kind: group-child +#CHECK: offset: 0 +#CHECK: target: f1 +#CHECK: - kind: group-child +#CHECK: offset: 0 +#CHECK: target: f2 +#CHECK: - kind: group-child +#CHECK: offset: 0 +#CHECK: target: d1 +#CHECK: - kind: group-child +#CHECK: offset: 0 +#CHECK: target: [[GCHILD:[a-zA-Z\.0-9_]+]] +#CHECK: - name: f1 +#CHECK: scope: global +#CHECK: references: +#CHECK: - kind: layout-after +#CHECK: offset: 0 +#CHECK: target: anotherfunction +#CHECK: - name: f2 +#CHECK: scope: global +#CHECK: - name: d1 +#CHECK: scope: global +#CHECK: type: data +#CHECK: - name: g1 +#CHECK: ref-name: [[GCHILD]] +#CHECK: scope: global +#CHECK: - name: anotherfunction +#CHECK: scope: global +#CHECK: type: data diff --git a/test/core/inline-coalesce.objtxt b/test/core/inline-coalesce.objtxt new file mode 100644 index 0000000000000..6df9d0e7a49a0 --- /dev/null +++ b/test/core/inline-coalesce.objtxt @@ -0,0 +1,31 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that non-inlined inlined functions are silently coalesced +# + +--- +defined-atoms: + - name: _inlineFunc + scope: global + type: code + merge: as-weak +--- +defined-atoms: + - name: _inlineFunc + scope: global + type: code + merge: as-weak +--- +defined-atoms: + - name: _inlineFunc + scope: global + type: code + merge: as-weak +... + + +# CHECK: name: _inlineFunc +# CHECK: merge: as-weak +# CHECK-NOT: name: _inlineFunc +# CHECK: ... diff --git a/test/core/multiple-def-error.objtxt b/test/core/multiple-def-error.objtxt new file mode 100644 index 0000000000000..7c7732c15fd9b --- /dev/null +++ b/test/core/multiple-def-error.objtxt @@ -0,0 +1,19 @@ +# RUN: not lld -core %s 2>&1 | FileCheck %s + +# +# Test that multiple definitions cause an error +# + +# CHECK: duplicate symbol + +--- +defined-atoms: + - name: _foo + scope: global + type: data +--- +defined-atoms: + - name: _foo + scope: global + type: data +... diff --git a/test/core/permissions.objtxt b/test/core/permissions.objtxt new file mode 100644 index 0000000000000..af33ea65c455a --- /dev/null +++ b/test/core/permissions.objtxt @@ -0,0 +1,57 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test permissions for known content types are implicit, but can be overridden. +# +--- +defined-atoms: + - name: one + type: code + + - name: two + type: data + permissions: rw- + + - name: three + type: const-data + + - name: four + type: unknown + + - name: oddCode + type: code + permissions: rwx + + - name: oddData + type: data + permissions: rwx + + - name: oddConstData + type: const-data + permissions: rw- + + - name: oddUnknown + type: unknown + permissions: rw- + +... + +# CHECK: --- +# CHECK: defined-atoms: +# CHECK: - name: one +# CHECK-NOT: permissions: +# CHECK: - name: two +# CHECK-NOT: permissions: +# CHECK: - name: three +# CHECK-NOT: permissions: +# CHECK: - name: four +# CHECK-NOT: permissions: +# CHECK: - name: oddCode +# CHECK: permissions: rwx +# CHECK: - name: oddData +# CHECK: permissions: rwx +# CHECK: - name: oddConstData +# CHECK: permissions: rw- +# CHECK: - name: oddUnknown +# CHECK: permissions: rw- +# CHECK: ... diff --git a/test/core/sectiongroup-deadstrip.objtxt b/test/core/sectiongroup-deadstrip.objtxt new file mode 100644 index 0000000000000..8606c52d62e13 --- /dev/null +++ b/test/core/sectiongroup-deadstrip.objtxt @@ -0,0 +1,88 @@ +# Test for section group members be preserved even if there is a +# reference to only one functions in the group. +# RUN: lld -core --dead-strip %s | FileCheck %s + +# +# Test that section groups are parsed properly when there is a reference to a +# group atom from outside a group. +# + +--- +defined-atoms: + - name: entry + dead-strip: never + references: + - offset: 1 + kind: pcrel32 + target: d1 + - name: f1 + scope: global + type: code + references: + - kind: layout-after + target: anotherfunction + - name: f2 + scope: global + type: code + - name: g1 + scope: global + type: code + - name: d1 + scope: global + type: data + - name: g1 + scope: global + type: group-comdat + references: + - kind: group-child + target: f1 + - kind: group-child + target: f2 + - kind: group-child + target: d1 + - kind: group-child + target: g1 + - name: anotherfunction + scope: global + type: data +--- +undefined-atoms: + - name: f1 + can-be-null: never +... + +#CHECK: defined-atoms: +#CHECK: - name: g1 +#CHECK: ref-name: [[PARENT:[a-zA-Z\.0-9_]+]] +#CHECK: scope: global +#CHECK: type: group-comdat +#CHECK: references: +#CHECK: - kind: group-child +#CHECK: offset: 0 +#CHECK: target: f1 +#CHECK: - kind: group-child +#CHECK: offset: 0 +#CHECK: target: f2 +#CHECK: - kind: group-child +#CHECK: offset: 0 +#CHECK: target: d1 +#CHECK: - kind: group-child +#CHECK: offset: 0 +#CHECK: target: [[GCHILD:[a-zA-Z\.0-9_]+]] +#CHECK: - name: f1 +#CHECK: scope: global +#CHECK: references: +#CHECK: - kind: layout-after +#CHECK: offset: 0 +#CHECK: target: anotherfunction +#CHECK: - name: f2 +#CHECK: scope: global +#CHECK: - name: d1 +#CHECK: scope: global +#CHECK: type: data +#CHECK: - name: g1 +#CHECK: ref-name: [[GCHILD]] +#CHECK: scope: global +#CHECK: - name: anotherfunction +#CHECK: scope: global +#CHECK: type: data diff --git a/test/core/sectiongroup-gnulinkonce-error.objtxt b/test/core/sectiongroup-gnulinkonce-error.objtxt new file mode 100644 index 0000000000000..47598957f8dfe --- /dev/null +++ b/test/core/sectiongroup-gnulinkonce-error.objtxt @@ -0,0 +1,64 @@ +# RUN: not lld -core %s 2>&1 | FileCheck %s + +# +# Test that section groups/gnu linkonce sections are parsed and a merge error +# is displayed at the time of symbol resolution. +# + +--- +defined-atoms: + - name: f1 + scope: global + type: code + - name: f2 + scope: global + type: code + - name: g1 + scope: global + type: code + - name: d1 + scope: global + type: data + - name: g1 + scope: global + type: group-comdat + references: + - kind: group-child + target: f1 + - kind: group-child + target: f2 + - kind: group-child + target: g1 + - kind: group-child + target: d1 +--- +defined-atoms: + - name: f1 + scope: global + type: code + - name: f2 + scope: global + type: code + - name: g1 + scope: global + type: code + - name: d1 + scope: global + type: data + - name: g1 + scope: global + type: gnu-linkonce + references: + - kind: group-child + target: f1 + - kind: group-child + target: f2 + - kind: group-child + target: g1 + - kind: group-child + target: d1 +... + +#CHECK: SymbolTable: error while merging g1 +#CHECK: LLVM ERROR: duplicate symbol error + diff --git a/test/core/sectiongroup-rearrange-resolve.objtxt b/test/core/sectiongroup-rearrange-resolve.objtxt new file mode 100644 index 0000000000000..7f5d2603775b5 --- /dev/null +++ b/test/core/sectiongroup-rearrange-resolve.objtxt @@ -0,0 +1,79 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that section groups are parsed and the first group selected for symbol +# resolution +# + +--- +defined-atoms: + - name: g1 + scope: global + type: group-comdat + references: + - kind: group-child + target: f1 + - kind: group-child + target: f2 + - kind: group-child + target: g1 + - kind: group-child + target: d1 + - name: f1 + scope: global + type: code + - name: f2 + scope: global + type: code + - name: g1 + scope: global + type: code + - name: d1 + scope: global + type: data +--- +defined-atoms: + - name: g1 + scope: global + type: group-comdat + references: + - kind: group-child + target: f1 + - kind: group-child + target: f2 + - kind: group-child + target: g1 + - kind: group-child + target: d1 + - name: f1 + scope: global + type: code + - name: f2 + scope: global + type: code + - name: g1 + scope: global + type: code + - name: d1 + scope: global + type: data +... + +# CHECK: defined-atoms: +# CHECK: - name: g1 +# CHECK: ref-name: [[PARENT:[a-zA-Z\.0-9_]+]] +# CHECK: type: group-comdat +# CHECK: references: +# CHECK: - kind: group-child +# CHECK: target: f1 +# CHECK: - kind: group-child +# CHECK: target: f2 +# CHECK: - kind: group-child +# CHECK: target: [[CHILD:[a-zA-Z\.0-9_]+]] +# CHECK: - kind: group-child +# CHECK: target: d1 +# CHECK: - name: f1 +# CHECK: - name: f2 +# CHECK: - name: g1 +# CHECK: ref-name: [[CHILD]] +# CHECK: - name: d1 diff --git a/test/core/sectiongroup-remaining-undef.objtxt b/test/core/sectiongroup-remaining-undef.objtxt new file mode 100644 index 0000000000000..7d889b8a49316 --- /dev/null +++ b/test/core/sectiongroup-remaining-undef.objtxt @@ -0,0 +1,80 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that section groups are parsed and the first group selected for symbol +# resolution. The second file which has the same group has a unresolved +# undefined symbol. lets make sure that the symbol is kept around in the final +# link and remains undefined. +# + +--- +defined-atoms: + - name: f1 + scope: global + type: code + - name: f2 + scope: global + type: code + - name: g1 + scope: global + type: code + - name: d1 + scope: global + type: data + - name: g1 + scope: global + type: group-comdat + references: + - kind: group-child + target: f1 + - kind: group-child + target: f2 + - kind: group-child + target: g1 + - kind: group-child + target: d1 +--- +defined-atoms: + - name: anotherfunction + scope: global + type: data + - name: f1 + scope: global + type: code + - name: f2 + scope: global + type: code + - name: f3 + scope: global + type: code + - name: g1 + scope: global + type: code + - name: d1 + scope: global + type: data + - name: g1 + scope: global + type: group-comdat + references: + - kind: group-child + target: f1 + - kind: group-child + target: f2 + - kind: group-child + target: f3 + - kind: group-child + target: g1 + - kind: group-child + target: d1 +--- +undefined-atoms: + - name: f3 + can-be-null: never +... + +#CHECK: - name: anotherfunction +#CHECK: scope: global +#CHECK: type: data +#CHECK: undefined-atoms: +#CHECK: - name: f3 diff --git a/test/core/sectiongroup-resolve.objtxt b/test/core/sectiongroup-resolve.objtxt new file mode 100644 index 0000000000000..2d481b1818b21 --- /dev/null +++ b/test/core/sectiongroup-resolve.objtxt @@ -0,0 +1,90 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that section groups are parsed and the first group selected for symbol +# resolution +# + +--- +defined-atoms: + - name: f1 + scope: global + type: code + - name: f2 + scope: global + type: code + - name: g1 + scope: global + type: code + - name: d1 + scope: global + type: data + - name: g1 + scope: global + type: group-comdat + references: + - kind: group-child + target: f1 + - kind: group-child + target: f2 + - kind: group-child + target: g1 + - kind: group-child + target: d1 +--- +defined-atoms: + - name: f1 + scope: global + type: code + - name: f2 + scope: global + type: code + - name: g1 + scope: global + type: code + - name: d1 + scope: global + type: data + - name: g1 + scope: global + type: group-comdat + references: + - kind: group-child + target: f1 + - kind: group-child + target: f2 + - kind: group-child + target: g1 + - kind: group-child + target: d1 +... + +#CHECK: defined-atoms: +#CHECK: - name: g1 +#CHECK: ref-name: [[PARENT:[a-zA-Z\.0-9_]+]] +#CHECK: scope: global +#CHECK: type: group-comdat +#CHECK: references: +#CHECK: - kind: group-child +#CHECK: offset: 0 +#CHECK: target: f1 +#CHECK: - kind: group-child +#CHECK: offset: 0 +#CHECK: target: f2 +#CHECK: - kind: group-child +#CHECK: offset: 0 +#CHECK: target: [[GCHILD:[a-zA-Z\.0-9_]+]] +#CHECK: - kind: group-child +#CHECK: offset: 0 +#CHECK: target: d1 +#CHECK: - name: f1 +#CHECK: scope: global +#CHECK: - name: f2 +#CHECK: scope: global +#CHECK: - name: g1 +#CHECK: ref-name: [[GCHILD]] +#CHECK: scope: global +#CHECK: - name: d1 +#CHECK: scope: global +#CHECK: type: data +#CHECK: ... diff --git a/test/core/sectiongroup-simple.objtxt b/test/core/sectiongroup-simple.objtxt new file mode 100644 index 0000000000000..9f0ff9581c1da --- /dev/null +++ b/test/core/sectiongroup-simple.objtxt @@ -0,0 +1,80 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that section groups are parsed properly when there is a reference to a +# group atom from outside a group. +# + +--- +defined-atoms: + - name: f1 + scope: global + type: code + references: + - kind: layout-after + target: anotherfunction + - name: f2 + scope: global + type: code + - name: g1 + scope: global + type: code + - name: d1 + scope: global + type: data + - name: g1 + scope: global + type: group-comdat + references: + - kind: group-child + target: f1 + - kind: group-child + target: f2 + - kind: group-child + target: d1 + - kind: group-child + target: g1 + - name: anotherfunction + scope: global + type: data +--- +undefined-atoms: + - name: f1 + can-be-null: never +... + +#CHECK: defined-atoms: +#CHECK: - name: g1 +#CHECK: ref-name: [[PARENT:[a-zA-Z\.0-9_]+]] +#CHECK: scope: global +#CHECK: type: group-comdat +#CHECK: references: +#CHECK: - kind: group-child +#CHECK: offset: 0 +#CHECK: target: f1 +#CHECK: - kind: group-child +#CHECK: offset: 0 +#CHECK: target: f2 +#CHECK: - kind: group-child +#CHECK: offset: 0 +#CHECK: target: d1 +#CHECK: - kind: group-child +#CHECK: offset: 0 +#CHECK: target: [[GCHILD:[a-zA-Z\.0-9_]+]] +#CHECK: - name: f1 +#CHECK: scope: global +#CHECK: references: +#CHECK: - kind: layout-after +#CHECK: offset: 0 +#CHECK: target: anotherfunction +#CHECK: - name: f2 +#CHECK: scope: global +#CHECK: - name: d1 +#CHECK: scope: global +#CHECK: type: data +#CHECK: - name: g1 +#CHECK: ref-name: [[GCHILD]] +#CHECK: scope: global +#CHECK: - name: anotherfunction +#CHECK: scope: global +#CHECK: type: data diff --git a/test/core/shared-library-basic.objtxt b/test/core/shared-library-basic.objtxt new file mode 100644 index 0000000000000..61445e7431fd4 --- /dev/null +++ b/test/core/shared-library-basic.objtxt @@ -0,0 +1,40 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that shared-library symbols are parsed and preserved +# + +--- +shared-library-atoms: + - name: malloc + load-name: libc.so + type: code + size: 0 + + - name: free + load-name: libc.so + + - name: fast_malloc + load-name: libc.so + can-be-null: at-runtime + + - name: stdout + load-name: libc.so + type: data + size: 8 + +... + +# CHECK: shared-library-atoms: +# CHECK: name: malloc +# CHECK: load-name: libc.so +# CHECK: name: free +# CHECK: load-name: libc.so +# CHECK: name: fast_malloc +# CHECK: load-name: libc.so +# CHECK: can-be-null: at-runtime +# CHECK: name: stdout +# CHECK: load-name: libc.so +# CHECK: type: data +# CHECK: size: 8 +# CHECK: ... diff --git a/test/core/shared-library-coalesce.objtxt b/test/core/shared-library-coalesce.objtxt new file mode 100644 index 0000000000000..51ff93e87a88f --- /dev/null +++ b/test/core/shared-library-coalesce.objtxt @@ -0,0 +1,84 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that shared library symbols preserve their attributes and merge properly +# + +--- +shared-library-atoms: + - name: foo1 + load-name: libc.so + + - name: foo2 + load-name: libc.so + + - name: bar1 + load-name: libc.so + can-be-null: at-runtime + + - name: bar2 + load-name: libc.so + can-be-null: at-runtime + + - name: mismatchNull1 + load-name: libc.so + can-be-null: at-runtime + + - name: mismatchNull2 + load-name: libc.so + + - name: mismatchload1 + load-name: liba.so + + - name: mismatchload2 + load-name: libb.so + +--- +shared-library-atoms: + - name: foo2 + load-name: libc.so + + - name: foo3 + load-name: libc.so + + - name: bar2 + load-name: libc.so + can-be-null: at-runtime + + - name: bar3 + load-name: libc.so + can-be-null: at-runtime + + - name: mismatchNull1 + load-name: libc.so + + - name: mismatchNull2 + load-name: libc.so + can-be-null: at-runtime + + - name: mismatchload1 + load-name: libb.so + + - name: mismatchload2 + load-name: liba.so + +... + +# CHECK: name: foo1 +# CHECK: name: foo2 +# CHECK: name: bar1 +# CHECK: can-be-null: at-runtime +# CHECK: name: bar2 +# CHECK: can-be-null: at-runtime +# CHECK: name: mismatchNull1 +# CHECK: can-be-null: at-runtime +# CHECK: name: mismatchNull2 +# CHECK-NOT: can-be-null: at-runtime +# CHECK: name: mismatchload1 +# CHECK: load-name: liba.so +# CHECK: name: mismatchload2 +# CHECK: load-name: libb.so +# CHECK: name: foo3 +# CHECK: name: bar3 +# CHECK: can-be-null: at-runtime +# CHECK: ... diff --git a/test/core/tent-merge.objtxt b/test/core/tent-merge.objtxt new file mode 100644 index 0000000000000..8ad46d40ae189 --- /dev/null +++ b/test/core/tent-merge.objtxt @@ -0,0 +1,25 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that a tentative definition and a regular global are merged into +# one regular global +# + +--- +defined-atoms: + - name: _foo + merge: as-tentative + scope: global + type: zero-fill + size: 4 +--- +defined-atoms: + - name: _foo + scope: global + type: data + content: [ 00, 00, 00, 00 ] +... + + +# CHECK: name: _foo +# CHECK-NOT: merge: as-tentative diff --git a/test/core/undef-coalesce-error.objtxt b/test/core/undef-coalesce-error.objtxt new file mode 100644 index 0000000000000..a0485befd2885 --- /dev/null +++ b/test/core/undef-coalesce-error.objtxt @@ -0,0 +1,47 @@ +# RUN: not lld -core --undefines-are-errors %s 2> %t.err +# RUN: FileCheck -check-prefix=CHECKERR %s < %t.err +# RUN: lld -core %s | FileCheck %s + +# +# Test that -undefines-are-errors triggers and error +# and that not using that option results in undefined atoms. +# + +--- +defined-atoms: + - name: foo + type: code + +undefined-atoms: + - name: malloc + - name: free +--- +defined-atoms: + - name: bar + type: code + +undefined-atoms: + - name: malloc + - name: myfunc +--- +defined-atoms: + - name: myfunc + scope: global + type: code + +undefined-atoms: + - name: free +... + +# CHECKERR: free +# CHECKERR: malloc +# CHECKERR: symbol(s) not found + +# CHECK: defined-atoms: +# CHECK: name: foo +# CHECK: name: bar +# CHECK: name: myfunc +# CHECK: undefined-atoms: +# CHECK: name: malloc +# CHECK: name: free +# CHECK: ... diff --git a/test/core/undef-coalesce.objtxt b/test/core/undef-coalesce.objtxt new file mode 100644 index 0000000000000..822ed5acf1915 --- /dev/null +++ b/test/core/undef-coalesce.objtxt @@ -0,0 +1,42 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that undefined symbols are coalesced with other undefined symbols +# and definitions override them. +# + +--- +defined-atoms: + - name: foo + type: code + +undefined-atoms: + - name: malloc + - name: free +--- +defined-atoms: + - name: bar + type: code + +undefined-atoms: + - name: malloc + - name: myfunc +--- +defined-atoms: + - name: myfunc + scope: global + type: code + +undefined-atoms: + - name: free +... + +# CHECK: defined-atoms: +# CHECK: name: foo +# CHECK: name: bar +# CHECK: name: myfunc +# CHECK: scope: global +# CHECK: undefined-atoms: +# CHECK: name: malloc +# CHECK: name: free +# CHECK: ... diff --git a/test/core/undef-fallback.objtxt b/test/core/undef-fallback.objtxt new file mode 100644 index 0000000000000..8abaa93c8e8d4 --- /dev/null +++ b/test/core/undef-fallback.objtxt @@ -0,0 +1,37 @@ +# RUN: lld -core %s | FileCheck %s + +# Test that fallback atoms can be parsed by YAML reader and processed by the +# core linker. + +--- +defined-atoms: + - name: def1 + scope: global + +undefined-atoms: + - name: undef1 + fallback: + name: fallback1 + - name: undef2 + fallback: + name: fallback2 +--- +defined-atoms: + - name: fallback1 + +undefined-atoms: + - name: def1 + fallback: + name: fallback3 +... + +# CHECK: defined-atoms: +# CHECK-NEXT: - name: def1 +# CHECK-NEXT: scope: global +# CHECK-NEXT: - name: fallback1 +# CHECK-NEXT: ref-name: fallback1 +# CHECK-NEXT: undefined-atoms: +# CHECK-NEXT: - name: fallback1 +# CHECK-NEXT: - name: fallback2 + +# CHECK-NOT: - name: fallback3 diff --git a/test/core/undef-weak-coalesce.objtxt b/test/core/undef-weak-coalesce.objtxt new file mode 100644 index 0000000000000..97e0fd28500d5 --- /dev/null +++ b/test/core/undef-weak-coalesce.objtxt @@ -0,0 +1,72 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that undefined symbols preserve their attributes and merge properly +# + +--- +undefined-atoms: + - name: regular_func + can-be-null: never + - name: weak_import_func + can-be-null: at-runtime + - name: weak_func + can-be-null: at-buildtime + - name: bar1 + can-be-null: never + - name: bar2 + can-be-null: at-runtime + - name: bar3 + can-be-null: at-buildtime + - name: bar4 + can-be-null: never + - name: bar5 + can-be-null: at-runtime + - name: bar6 + can-be-null: at-buildtime + - name: bar7 + can-be-null: never + - name: bar8 + can-be-null: at-runtime + - name: bar9 + can-be-null: at-buildtime +--- +undefined-atoms: + - name: bar1 + can-be-null: never + - name: bar2 + can-be-null: at-runtime + - name: bar3 + can-be-null: at-buildtime + - name: bar4 + can-be-null: at-runtime + - name: bar5 + can-be-null: at-buildtime + - name: bar6 + can-be-null: never + - name: bar7 + can-be-null: at-buildtime + - name: bar8 + can-be-null: never + - name: bar9 + can-be-null: at-runtime +... + +# CHECK: - name: regular_func +# CHECK-NEXT: - name: weak_import_func +# CHECK-NEXT: can-be-null: at-runtime +# CHECK-NEXT: - name: weak_func +# CHECK-NEXT: can-be-null: at-buildtime +# CHECK-NEXT: - name: bar1 +# CHECK-NEXT: - name: bar2 +# CHECK-NEXT: can-be-null: at-runtime +# CHECK-NEXT: - name: bar3 +# CHECK-NEXT: can-be-null: at-buildtime +# CHECK-NEXT: - name: bar4 +# CHECK-NEXT: - name: bar5 +# CHECK-NEXT: can-be-null: at-runtime +# CHECK-NEXT: - name: bar7 +# CHECK-NEXT: - name: bar6 +# CHECK-NEXT: - name: bar8 +# CHECK-NEXT: - name: bar9 +# CHECK-NEXT: can-be-null: at-runtime diff --git a/test/core/weak-coalesce.objtxt b/test/core/weak-coalesce.objtxt new file mode 100644 index 0000000000000..550c10cfa468c --- /dev/null +++ b/test/core/weak-coalesce.objtxt @@ -0,0 +1,30 @@ +# RUN: lld -core %s | FileCheck %s + +# +# Test that weak definitions are coalesced away in favor of a regular definition +# + +--- +defined-atoms: + - name: _foo + merge: as-weak + scope: global + type: data +--- +defined-atoms: + - name: _foo + scope: global + type: data +--- +defined-atoms: + - name: _foo + merge: as-weak + scope: global + type: data +... + + +# CHECK: name: _foo +# CHECK-NOT: merge: as-weak +# CHECK-NOT: name: _foo +# CHECK: ... diff --git a/test/darwin/native-and-mach-o.objtxt b/test/darwin/native-and-mach-o.objtxt new file mode 100644 index 0000000000000..20f2d0b962070 --- /dev/null +++ b/test/darwin/native-and-mach-o.objtxt @@ -0,0 +1,65 @@ +# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t && \ +# RUN: llvm-nm %t | FileCheck %s +# +# Test a mix of atoms and mach-o both encoded in yaml +# + +--- !native +defined-atoms: + - name: _main + type: code + scope: global + content: [ 55, 48, 89, E5, 30, C0, E8, 00, + 00, 00, 00, 31, C0, 5D, C3 ] + references: + - offset: 7 + kind: branch32 + target: _foo + +undefined-atoms: + - name: _foo + + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS ] + address: 0 + content: [ 0xC3 ] +global-symbols: + - name: _foo + type: N_SECT + scope: [ N_EXT ] + sect: 1 + desc: [ ] + value: 0 + +--- !mach-o +arch: x86_64 +file-type: MH_DYLIB +flags: [ ] +install-name: /usr/lib/libSystem.B.dylib +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55 ] + +global-symbols: + - name: dyld_stub_binder + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + + +... + +# CHECK: {{[0-9a-f]+}} T _foo +# CHECK: {{[0-9a-f]+}} T _main diff --git a/test/elf/AArch64/Inputs/fn.c b/test/elf/AArch64/Inputs/fn.c new file mode 100644 index 0000000000000..54939a2426b2e --- /dev/null +++ b/test/elf/AArch64/Inputs/fn.c @@ -0,0 +1,4 @@ +int fn() +{ + return 0; +} diff --git a/test/elf/AArch64/Inputs/fn.o b/test/elf/AArch64/Inputs/fn.o Binary files differnew file mode 100644 index 0000000000000..53e47ad37742d --- /dev/null +++ b/test/elf/AArch64/Inputs/fn.o diff --git a/test/elf/AArch64/Inputs/initfini-option.c b/test/elf/AArch64/Inputs/initfini-option.c new file mode 100644 index 0000000000000..6021fb57ffa9c --- /dev/null +++ b/test/elf/AArch64/Inputs/initfini-option.c @@ -0,0 +1,12 @@ +#include <stdio.h> + +void init() { + printf("%s\n", __FUNCTION__); +} + +void fini() { + printf("%s\n", __FUNCTION__); +} + +int main() { +} diff --git a/test/elf/AArch64/Inputs/initfini-option.o b/test/elf/AArch64/Inputs/initfini-option.o Binary files differnew file mode 100644 index 0000000000000..c75079b013fad --- /dev/null +++ b/test/elf/AArch64/Inputs/initfini-option.o diff --git a/test/elf/AArch64/Inputs/initfini.c b/test/elf/AArch64/Inputs/initfini.c new file mode 100644 index 0000000000000..8369d68a8daba --- /dev/null +++ b/test/elf/AArch64/Inputs/initfini.c @@ -0,0 +1,13 @@ +#include <stdio.h> + +void __attribute__ ((constructor)) constructor() { + printf("%s\n", __FUNCTION__); +} + +void __attribute__ ((destructor)) destructor() { + printf("%s\n", __FUNCTION__); +} + +int main() { + return 0; +} diff --git a/test/elf/AArch64/Inputs/initfini.o b/test/elf/AArch64/Inputs/initfini.o Binary files differnew file mode 100644 index 0000000000000..030fe59878bee --- /dev/null +++ b/test/elf/AArch64/Inputs/initfini.o diff --git a/test/elf/AArch64/Inputs/main.c b/test/elf/AArch64/Inputs/main.c new file mode 100644 index 0000000000000..0280c91270761 --- /dev/null +++ b/test/elf/AArch64/Inputs/main.c @@ -0,0 +1,4 @@ +int main() { + fn(); + return 0; +} diff --git a/test/elf/AArch64/Inputs/main.o b/test/elf/AArch64/Inputs/main.o Binary files differnew file mode 100644 index 0000000000000..8c0f049da6a82 --- /dev/null +++ b/test/elf/AArch64/Inputs/main.o diff --git a/test/elf/AArch64/Inputs/no-interp-section.c b/test/elf/AArch64/Inputs/no-interp-section.c new file mode 100644 index 0000000000000..3981c038ed334 --- /dev/null +++ b/test/elf/AArch64/Inputs/no-interp-section.c @@ -0,0 +1 @@ +int c = 10; diff --git a/test/elf/AArch64/Inputs/no-interp-section.o b/test/elf/AArch64/Inputs/no-interp-section.o Binary files differnew file mode 100644 index 0000000000000..36b41fdbf782a --- /dev/null +++ b/test/elf/AArch64/Inputs/no-interp-section.o diff --git a/test/elf/AArch64/Inputs/zerosizedsection.o b/test/elf/AArch64/Inputs/zerosizedsection.o Binary files differnew file mode 100644 index 0000000000000..10123fcef90de --- /dev/null +++ b/test/elf/AArch64/Inputs/zerosizedsection.o diff --git a/test/elf/AArch64/Inputs/zerosizedsection.s b/test/elf/AArch64/Inputs/zerosizedsection.s new file mode 100644 index 0000000000000..651ee3aab5033 --- /dev/null +++ b/test/elf/AArch64/Inputs/zerosizedsection.s @@ -0,0 +1,3 @@ +.text +.data +.word .text diff --git a/test/elf/AArch64/defsym.test b/test/elf/AArch64/defsym.test new file mode 100644 index 0000000000000..8bf492d4a38aa --- /dev/null +++ b/test/elf/AArch64/defsym.test @@ -0,0 +1,22 @@ +RUN: lld -flavor gnu -target aarch64--linux-gnu --defsym=main=fn \ +RUN: --noinhibit-exec %p/Inputs/fn.o -o %t +RUN: llvm-readobj -symbols %t | FileCheck %s + +CHECK: Symbol { +CHECK: Name: main (1) +CHECK: Value: 0x4001A4 +CHECK: Size: 0 +CHECK: Binding: Global (0x1) +CHECK: Type: Function (0x2) +CHECK: Other: 0 +CHECK: Section: .text (0x5) +CHECK: } +CHECK: Symbol { +CHECK: Name: fn (11) +CHECK: Value: 0x4001A4 +CHECK: Size: 8 +CHECK: Binding: Global (0x1) +CHECK: Type: Function (0x2) +CHECK: Other: 0 +CHECK: Section: .text (0x5) +CHECK: } diff --git a/test/elf/AArch64/dontignorezerosize-sections.test b/test/elf/AArch64/dontignorezerosize-sections.test new file mode 100644 index 0000000000000..ac593abcc3682 --- /dev/null +++ b/test/elf/AArch64/dontignorezerosize-sections.test @@ -0,0 +1,9 @@ +# This tests that lld is not ignoring zero sized sections +RUN: lld -flavor gnu -target aarch64--linux-gnu %p/Inputs/zerosizedsection.o \ +RUN: --noinhibit-exec --output-filetype=yaml -o %t +RUN: FileCheck %s < %t + +CHECK: references: +CHECK: - kind: layout-after +CHECK: offset: 0 +CHECK: target: L000 diff --git a/test/elf/AArch64/dynlib-nointerp-section.test b/test/elf/AArch64/dynlib-nointerp-section.test new file mode 100644 index 0000000000000..9365b4d5b3e97 --- /dev/null +++ b/test/elf/AArch64/dynlib-nointerp-section.test @@ -0,0 +1,5 @@ +RUN: lld -flavor gnu -target aarch64--linux-gnu %p/Inputs/no-interp-section.o \ +RUN: -o %t -shared +RUN: llvm-objdump -section-headers %t | FileCheck %s + +CHECK-NOT: .interp diff --git a/test/elf/AArch64/initfini.test b/test/elf/AArch64/initfini.test new file mode 100644 index 0000000000000..887e44ea105b7 --- /dev/null +++ b/test/elf/AArch64/initfini.test @@ -0,0 +1,23 @@ +# This tests the functionality that lld is able to read +# init_array/fini_array sections in the input ELF. This +# corresponds to the the .init_array/.fini_array sections +# in the output ELF. + +RUN: lld -flavor gnu -target aarch64--linux-gnu %p/Inputs/initfini.o \ +RUN: --noinhibit-exec --output-filetype=yaml -o %t +RUN: FileCheck %s < %t + +CHECK: type: data +CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ] +CHECK: section-name: .init_array +CHECK: references: +CHECK: - kind: R_AARCH64_ABS64 +CHECK: offset: 0 +CHECK: target: constructor +CHECK: type: data +CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ] +CHECK: section-name: .fini_array +CHECK: references: +CHECK: - kind: R_AARCH64_ABS64 +CHECK: offset: 0 +CHECK: target: destructor diff --git a/test/elf/AArch64/rel-abs32-overflow.test b/test/elf/AArch64/rel-abs32-overflow.test new file mode 100644 index 0000000000000..e32fce173c512 --- /dev/null +++ b/test/elf/AArch64/rel-abs32-overflow.test @@ -0,0 +1,53 @@ +# Check handling of R_AARCH64_ABS32 relocation overflow. +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: not lld -flavor gnu -target arm64 -o %t-exe %t-obj 2>&1 | FileCheck %s + +# CHECK-DAG: Relocation out of range in file {{.*}}: reference from data1+0 to data2+34359738369 of type 258 (R_AARCH64_ABS32) +# CHECK-DAG: Relocation out of range in file {{.*}}: reference from data2+0 to data1+34359738369 of type 258 (R_AARCH64_ABS32) + +!ELF +FileHeader: !FileHeader + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_AARCH64 + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "00000000" + AddressAlign: 16 + Flags: [SHF_ALLOC, SHF_EXECINSTR] +- Name: .data + Type: SHT_PROGBITS + Content: "0000000000000000" + AddressAlign: 16 + Flags: [SHF_ALLOC, SHF_WRITE] + +- Name: .rela.data + Type: SHT_RELA + Info: .data + AddressAlign: 8 + Relocations: + - Offset: 0x0 + Symbol: data2 + Type: R_AARCH64_ABS32 + Addend: 0x800000001 + - Offset: 0x4 + Symbol: data1 + Type: R_AARCH64_ABS32 + Addend: 0x800000001 + +Symbols: + Global: + - Name: _start + Section: .text + Value: 0x0 + Size: 4 + - Name: data1 + Section: .data + Size: 4 + - Name: data2 + Section: .data + Value: 0x4 + Size: 4 diff --git a/test/elf/AArch64/rel-abs32.test b/test/elf/AArch64/rel-abs32.test new file mode 100644 index 0000000000000..edd7b69e428dc --- /dev/null +++ b/test/elf/AArch64/rel-abs32.test @@ -0,0 +1,59 @@ +# Check handling of R_AARCH64_ABS32 relocation. +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: lld -flavor gnu -target arm64 -o %t-exe %t-obj +# RUN: llvm-objdump -s -t %t-exe | FileCheck %s + +# CHECK: Contents of section .data: +# CHECK-NEXT: 401060 65104080 61104080 e.@.a.@. +# ^^ data2 + 0x80000001 = 0x80401069 +# ^^ data1 + 0x80000001 = 0x80401061 +# CHECK: SYMBOL TABLE: +# CHECK: 00401060 g .data 00000004 data1 +# CHECK: 00401064 g .data 00000004 data2 + +!ELF +FileHeader: !FileHeader + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_AARCH64 + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "00000000" + AddressAlign: 16 + Flags: [SHF_ALLOC, SHF_EXECINSTR] +- Name: .data + Type: SHT_PROGBITS + Content: "0000000000000000" + AddressAlign: 16 + Flags: [SHF_ALLOC, SHF_WRITE] + +- Name: .rela.data + Type: SHT_RELA + Info: .data + AddressAlign: 8 + Relocations: + - Offset: 0x0 + Symbol: data2 + Type: R_AARCH64_ABS32 + Addend: 0x80000001 + - Offset: 0x4 + Symbol: data1 + Type: R_AARCH64_ABS32 + Addend: 0x80000001 + +Symbols: + Global: + - Name: _start + Section: .text + Value: 0x0 + Size: 4 + - Name: data1 + Section: .data + Size: 4 + - Name: data2 + Section: .data + Value: 0x4 + Size: 4 diff --git a/test/elf/AArch64/rel-abs64.test b/test/elf/AArch64/rel-abs64.test new file mode 100644 index 0000000000000..c125e3f2450d4 --- /dev/null +++ b/test/elf/AArch64/rel-abs64.test @@ -0,0 +1,59 @@ +# Check handling of R_AARCH64_ABS64 relocation. +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: lld -flavor gnu -target arm64 -o %t-exe %t-obj +# RUN: llvm-objdump -s -t %t-exe | FileCheck %s + +# CHECK: Contents of section .data: +# CHECK-NEXT: 401060 69104000 00000080 61104000 00000080 i.@.....a.@..... +# ^^ data2 + 0x8000000000000001 = 0x8000000000401069 +# ^^ data1 + 0x8000000000000001 = 0x8000000000401061 +# CHECK: SYMBOL TABLE: +# CHECK: 00401060 g .data 00000008 data1 +# CHECK: 00401068 g .data 00000008 data2 + +!ELF +FileHeader: !FileHeader + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_AARCH64 + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "00000000" + AddressAlign: 16 + Flags: [SHF_ALLOC, SHF_EXECINSTR] +- Name: .data + Type: SHT_PROGBITS + Content: "00000000000000000000000000000000" + AddressAlign: 16 + Flags: [SHF_ALLOC, SHF_WRITE] + +- Name: .rela.data + Type: SHT_RELA + Info: .data + AddressAlign: 8 + Relocations: + - Offset: 0x0 + Symbol: data2 + Type: R_AARCH64_ABS64 + Addend: -9223372036854775807 + - Offset: 0x8 + Symbol: data1 + Type: R_AARCH64_ABS64 + Addend: -9223372036854775807 + +Symbols: + Global: + - Name: _start + Section: .text + Value: 0x0 + Size: 4 + - Name: data1 + Section: .data + Size: 8 + - Name: data2 + Section: .data + Value: 0x8 + Size: 8 diff --git a/test/elf/AArch64/rel-bad.test b/test/elf/AArch64/rel-bad.test new file mode 100644 index 0000000000000..6b9e831146f3a --- /dev/null +++ b/test/elf/AArch64/rel-bad.test @@ -0,0 +1,44 @@ +# Check handling of a bad relocation (in this case dynamic in a static object). +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: not lld -flavor gnu -target arm64 -o %t-exe %t-obj 2>&1 | FileCheck %s + +# CHECK: Unhandled reference type in file {{.*}}: reference from data1+4 to data1+0 of type 1024 (R_AARCH64_COPY) + +!ELF +FileHeader: !FileHeader + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_AARCH64 + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "00000000" + AddressAlign: 16 + Flags: [SHF_ALLOC, SHF_EXECINSTR] +- Name: .data + Type: SHT_PROGBITS + Content: "0000000000000000" + AddressAlign: 16 + Flags: [SHF_ALLOC, SHF_WRITE] + +- Name: .rela.data + Type: SHT_RELA + Info: .data + AddressAlign: 8 + Relocations: + - Offset: 0x4 + Symbol: data1 + Type: R_AARCH64_COPY + Addend: 0 + +Symbols: + Global: + - Name: _start + Section: .text + Value: 0x0 + Size: 4 + - Name: data1 + Section: .data + Size: 8 diff --git a/test/elf/ARM/arm-symbols.test b/test/elf/ARM/arm-symbols.test new file mode 100644 index 0000000000000..b99960697679c --- /dev/null +++ b/test/elf/ARM/arm-symbols.test @@ -0,0 +1,52 @@ +# Check that symbols formed from ARM instructions are valid: +# 1. Symbol address. +# 2. Symbol content size. +# 3. Symbol content. + +# RUN: yaml2obj -format=elf %s > %t-a.o +# RUN: lld -flavor gnu -target arm-linux-gnu \ +# RUN: -Bstatic --noinhibit-exec %t-a.o -o %t-a +# RUN: llvm-readobj -symbols %t-a | FileCheck -check-prefix=SYM-ADDR %s +# RUN: llvm-readobj -symbols %t-a | FileCheck -check-prefix=SYM-SIZE %s +# RUN: llvm-objdump -s -t %t-a | FileCheck -check-prefix=SYM-CONTENT %s + +# SYM-ADDR: Name: main (1) +# SYM-ADDR-NEXT: Value: 0x400074 + +# SYM-SIZE: Name: main (1) +# SYM-SIZE-NEXT: Value: 0x{{[0-9a-f]+}} +# SYM-SIZE-NEXT: Size: 28 + +# SYM-CONTENT: Contents of section .text: +# SYM-CONTENT-NEXT: 400074 04b02de5 00b08de2 0030a0e3 0300a0e1 ..-......0...... +# SYM-CONTENT-NEXT: 400084 00d04be2 04b09de4 1eff2fe1 ..K......./. + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 04B02DE500B08DE20030A0E30300A0E100D04BE204B09DE41EFF2FE1 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: main + Type: STT_FUNC + Section: .text +... diff --git a/test/elf/ARM/defsym.test b/test/elf/ARM/defsym.test new file mode 100644 index 0000000000000..0cd1736d924e8 --- /dev/null +++ b/test/elf/ARM/defsym.test @@ -0,0 +1,51 @@ +# Check that defined symbols are present in the generated executable + +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target arm-linux-gnu --defsym=main=fn \ +# RUN: -Bstatic --noinhibit-exec %t-o.o -o %t +# RUN: llvm-readobj -symbols %t | FileCheck %s + +# CHECK: Name: main (1) +# CHECK-NEXT: Value: 0x400074 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text (0x1) +# CHECK: Name: fn (6) +# CHECK-NEXT: Value: 0x400074 +# CHECK-NEXT: Size: {{[0-9]+}} +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text (0x1) + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 04B02DE500B08DE20030A0E30300A0E100D04BE204B09DE41EFF2FE1 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: fn + Type: STT_FUNC + Section: .text +... diff --git a/test/elf/ARM/entry-point.test b/test/elf/ARM/entry-point.test new file mode 100644 index 0000000000000..d168bb6463c66 --- /dev/null +++ b/test/elf/ARM/entry-point.test @@ -0,0 +1,77 @@ +# 1. Check entry point address for ARM code - should be even. +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-arm.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-arm.o -o %t-arm +# RUN: llvm-readobj -file-headers %t-arm | FileCheck -check-prefix=ARM-ENTRY %s +# +# ARM-ENTRY: Entry: 0x400074 + +# 2. Check entry point address for Thumb code - should be odd. +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-thm.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-thm.o -o %t-thm +# RUN: llvm-readobj -file-headers %t-thm | FileCheck -check-prefix=THM-ENTRY %s +# +# THM-ENTRY: Entry: 0x400075 + +# arm.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 04B02DE500B08DE20030A0E30300A0E100D04BE204B09DE41EFF2FE1 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: _start + Type: STT_FUNC + Section: .text + +# thm.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 80B400AF00231846BD465DF8047B7047 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: _start + Type: STT_FUNC + Section: .text + Value: 0x0000000000000001 +... diff --git a/test/elf/ARM/missing-symbol.test b/test/elf/ARM/missing-symbol.test new file mode 100644 index 0000000000000..f9b3e26ea1a31 --- /dev/null +++ b/test/elf/ARM/missing-symbol.test @@ -0,0 +1,39 @@ +# Check that _MISSING_SYMBOL_ symbol is not resolved + +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-o.o -o %t 2>&1 | FileCheck %s + +# CHECK: Undefined symbol: {{.*}}: _MISSING_SYMBOL_ + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 80B483B000AF40F20003C0F200037B60002318460C37BD465DF8047B704700BF + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: main + Type: STT_FUNC + Section: .text + Value: 0x0000000000000001 + - Name: _MISSING_SYMBOL_ +... diff --git a/test/elf/ARM/rel-abs32.test b/test/elf/ARM/rel-abs32.test new file mode 100644 index 0000000000000..57d323100feb2 --- /dev/null +++ b/test/elf/ARM/rel-abs32.test @@ -0,0 +1,59 @@ +# Check handling of R_ARM_ABS32 relocation. +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-o.o -o %t +# RUN: llvm-objdump -s -t %t | FileCheck %s + +# CHECK: Contents of section .data: +# CHECK-NEXT: 401000 84004000 +# data = 0x400084 ^^ +# data main addr content +# 0x400084 = 0x400074 + 0x10 +# CHECK: SYMBOL TABLE: +# CHECK: 00400074 g F .text {{[0-9a-f]+}} main +# CHECK: 00401000 g .data 00000004 data + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 04B02DE500B08DE20030A0E30300A0E100D04BE204B09DE41EFF2FE1 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '10000000' + - Name: .rel.data + Type: SHT_REL + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: .data + Relocations: + - Offset: 0x0000000000000000 + Symbol: main + Type: R_ARM_ABS32 + Addend: 0 + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: main + Type: STT_FUNC + Section: .text + Size: 0x000000000000001C + - Name: data + Type: STT_OBJECT + Section: .data + Size: 0x0000000000000004 +... diff --git a/test/elf/ARM/rel-arm-call.test b/test/elf/ARM/rel-arm-call.test new file mode 100644 index 0000000000000..234b34eae92da --- /dev/null +++ b/test/elf/ARM/rel-arm-call.test @@ -0,0 +1,60 @@ +# Check handling of R_ARM_CALL relocation. +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-o.o -o %t +# RUN: llvm-objdump -s -t %t | FileCheck %s + +# CHECK: Contents of section .text: +# CHECK: 400084 1eff2fe1 00482de9 04b08de2 f7ffffeb +# offset = -0x24 ^^ +# call site offset PC(arm) _Z1fv addr +# 0x400090 + (-0x24) + 0x8 = 0x400074 +# CHECK: SYMBOL TABLE: +# CHECK: 00400074 g F .text {{[0-9a-f]+}} _Z1fv +# CHECK: 00400088 g F .text {{[0-9a-f]+}} main + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 04B02DE500B08DE200D04BE204B09DE41EFF2FE100482DE904B08DE2FEFFFFEB0030A0E30300A0E10088BDE8 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: .text + Relocations: + - Offset: 0x000000000000001C + Symbol: _Z1fv + Type: R_ARM_CALL + Addend: 0 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: _Z1fv + Type: STT_FUNC + Section: .text + Size: 0x0000000000000014 + - Name: main + Type: STT_FUNC + Section: .text + Value: 0x0000000000000014 + Size: 0x0000000000000018 +... diff --git a/test/elf/ARM/rel-arm-jump24-veneer-b.test b/test/elf/ARM/rel-arm-jump24-veneer-b.test new file mode 100644 index 0000000000000..4caeac083b8db --- /dev/null +++ b/test/elf/ARM/rel-arm-jump24-veneer-b.test @@ -0,0 +1,101 @@ +# Check veneer generation for R_ARM_JUMP24 relocation (B instruction call). + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-arm.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-thm.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-arm.o %t-thm.o -o %t + +# RUN: llvm-objdump -s -t %t | FileCheck -check-prefix=B-VENEER %s +# RUN: llvm-objdump -s -t %t | FileCheck -check-prefix=B-ADDR %s + +# B-VENEER: Contents of section .text: +# B-VENEER: 400074 010000ea +# Call from main: +# offset = 0x4 ^^ +# call site offset PC(arm) ___Z1fv_from_arm addr +# 0x400074 + 0x4 + 0x8 = 0x400080 +# +# Code of the veneer: +# B-VENEER: {{[0-9a-f]+}} {{[0-9a-f]+}} 04f01fe5 +# B-VENEER: 400084 79004000 +# call addr = 0x400079 ^^ +# call addr _Z1fv addr Thumb mode +# 0x400079 = 0x400078 | 0x1 +# +# B-ADDR: SYMBOL TABLE: +# B-ADDR: 00400080 l F .text {{[0-9a-f]+}} ___Z1fv_from_arm +# B-ADDR: 00400074 g F .text {{[0-9a-f]+}} main +# B-ADDR: 00400078 g F .text {{[0-9a-f]+}} _Z1fv + +# arm.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: FEFFFFEA + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: .text + Relocations: + - Offset: 0x0000000000000000 + Symbol: _Z1fv + Type: R_ARM_JUMP24 + Addend: 0 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: main + Type: STT_FUNC + Section: .text + - Name: _Z1fv + +# thm.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 4FF0000318467047 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: _Z1fv + Type: STT_FUNC + Section: .text + Value: 0x0000000000000001 +... diff --git a/test/elf/ARM/rel-arm-jump24-veneer-bl.test b/test/elf/ARM/rel-arm-jump24-veneer-bl.test new file mode 100644 index 0000000000000..cd386dc5b1a48 --- /dev/null +++ b/test/elf/ARM/rel-arm-jump24-veneer-bl.test @@ -0,0 +1,100 @@ +# Check veneer generation for R_ARM_JUMP24 relocation (BL<c> instruction call). + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-arm.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-thm.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-arm.o %t-thm.o -o %t + +# RUN: llvm-objdump -s -t %t | FileCheck -check-prefix=BL-VENEER %s +# RUN: llvm-objdump -s -t %t | FileCheck -check-prefix=BL-ADDR %s + +# BL-VENEER: Contents of section .text: +# BL-VENEER: 400084 0400000b +# Call from main: +# offset = 0x10 ^^ +# call site offset PC(arm) ___Z1fv_from_arm addr +# 0x400084 + 0x10 + 0x8 = 0x40009c +# +# Code of the veneer: +# BL-VENEER: 400094 {{[0-9a-f]+}} {{[0-9a-f]+}} {{[0-9a-f]+}} 95004000 +# call addr = 0x400095 ^^ +# call addr _Z1fv addr Thumb mode +# 0x400095 = 0x400094 | 0x1 +# +# BL-ADDR: SYMBOL TABLE: +# BL-ADDR: 0040009c l F .text {{[0-9a-f]+}} ___Z1fv_from_arm +# BL-ADDR: 00400074 g F .text {{[0-9a-f]+}} main +# BL-ADDR: 00400094 g F .text {{[0-9a-f]+}} _Z1fv + +# arm.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 00482DE904B08DE20030A0E3000053E3FEFFFF0B0030A0E30300A0E10088BDE8 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: .text + Relocations: + - Offset: 0x0000000000000010 + Symbol: _Z1fv + Type: R_ARM_JUMP24 + Addend: 0 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: main + Type: STT_FUNC + Section: .text + - Name: _Z1fv + +# thm.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 4FF0000318467047 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: _Z1fv + Type: STT_FUNC + Section: .text + Value: 0x0000000000000001 +... diff --git a/test/elf/ARM/rel-arm-jump24.test b/test/elf/ARM/rel-arm-jump24.test new file mode 100644 index 0000000000000..18eb8397d7b55 --- /dev/null +++ b/test/elf/ARM/rel-arm-jump24.test @@ -0,0 +1,58 @@ +# Check handling of R_ARM_JUMP24 relocation. +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-o.o -o %t +# RUN: llvm-objdump -s -t %t | FileCheck %s + +# CHECK: Contents of section .text: +# CHECK: 400094 04b08de2 f5ffffea 0030a0e1 0300a0e1 +# offset = -0x2C ^^ +# call site offset PC(arm) _Z1fv addr +# 0x400098 + (-0x2C) + 0x8 = 0x400074 +# CHECK: SYMBOL TABLE: +# CHECK: 00400074 g F .text {{[0-9a-f]+}} _Z1fv +# CHECK: 00400090 g F .text {{[0-9a-f]+}} main + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 04B02DE500B08DE20030A0E30300A0E100D04BE204B09DE41EFF2FE100482DE904B08DE2FEFFFFEA0030A0E10300A0E10088BDE8 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: .text + Relocations: + - Offset: 0x0000000000000024 + Symbol: _Z1fv + Type: R_ARM_JUMP24 + Addend: 0 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: _Z1fv + Type: STT_FUNC + Section: .text + - Name: main + Type: STT_FUNC + Section: .text + Value: 0x000000000000001C +... diff --git a/test/elf/ARM/rel-arm-mov.test b/test/elf/ARM/rel-arm-mov.test new file mode 100644 index 0000000000000..e50aea4bb2069 --- /dev/null +++ b/test/elf/ARM/rel-arm-mov.test @@ -0,0 +1,64 @@ +# Check handling of R_ARM_MOVW_ABS_NC and R_ARM_MOVT_ABS relocation pair. +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-o.o -o %t +# RUN: llvm-objdump -s -t %t | FileCheck %s + +# CHECK: Contents of section .text: +# CHECK: 400074 04b02de5 00b08de2 003001e3 403040e3 +# addrL = 0x1000 ^^ +# addrH = 0x40 ^^ +# addrH addrL _ZL5data1 addr +# (0x40 << 16) + 0x1000 = 0x401000 +# CHECK: SYMBOL TABLE: +# CHECK: 00401000 l .bss 00000004 _ZL5data1 +# CHECK: 00400074 g F .text {{[0-9a-f]+}} main + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 04B02DE500B08DE2003000E3003040E30A20A0E3002083E50030A0E30300A0E100D04BE204B09DE41EFF2FE1 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: .text + Relocations: + - Offset: 0x0000000000000008 + Symbol: _ZL5data1 + Type: R_ARM_MOVW_ABS_NC + Addend: 0 + - Offset: 0x000000000000000C + Symbol: _ZL5data1 + Type: R_ARM_MOVT_ABS + Addend: 0 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: 411C0000 +Symbols: + Local: + - Name: _ZL5data1 + Type: STT_OBJECT + Section: .bss + Size: 0x0000000000000004 + Global: + - Name: main + Type: STT_FUNC + Section: .text +... diff --git a/test/elf/ARM/rel-arm-prel31.test b/test/elf/ARM/rel-arm-prel31.test new file mode 100644 index 0000000000000..1effc4eceb0e4 --- /dev/null +++ b/test/elf/ARM/rel-arm-prel31.test @@ -0,0 +1,47 @@ +# Check handling of R_ARM_PREL31 relocation. +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-o.o -o %t +# RUN: llvm-objdump -s -t %t | FileCheck %s + +# CHECK: Contents of section .ARM.extab: +# CHECK: 4000a4 b1fffe7f +# CHECK: SYMBOL TABLE: +# CHECK: 00400054 g F .text {{[0-9a-f]+}} __gxx_personality_v0 + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .ARM.extab + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x4 + Content: 0000FF7F84019701B0B0B008FFFF01080E2432003A040000 + - Name: .rel.ARM.extab + Type: SHT_REL + Link: .symtab + AddressAlign: 0x4 + Info: .ARM.extab + Relocations: + - Offset: 0 + Symbol: __gxx_personality_v0 + Type: R_ARM_PREL31 + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x4 + Content: 80B400AF00231846BD465DF8047B704780B582B000AF3B1D1846FFF7FEFFFFF7FEFFFFF7FEFF0420FFF7FEFF0346184601230360002240F20001C0F20001FFF7FEFF3B1D1846FFF7FEFFFFF7FEFF00BF +Symbols: + Local: + Global: + - Name: __gxx_personality_v0 + Type: STT_FUNC + Section: .text + Value: 0x1 +... + diff --git a/test/elf/ARM/rel-arm-thm-interwork.test b/test/elf/ARM/rel-arm-thm-interwork.test new file mode 100644 index 0000000000000..8ee9186b48db8 --- /dev/null +++ b/test/elf/ARM/rel-arm-thm-interwork.test @@ -0,0 +1,123 @@ +# Check ARM <=> Thumb interwork. +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-arm.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-thm.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-arm.o %t-thm.o -o %t + +# Check R_ARM_CALL veneer to call Thumb code +# RUN: llvm-objdump -s -t %t | FileCheck -check-prefix=ARM-CALL %s + +# ARM-CALL: Contents of section .text: +# ARM-CALL: 400074 00482de9 04b08de2 000000fa 0088bde8 +# offset = 0x0 ^^ +# call site offset PC(arm) _Z2f2v addr +# 0x40007C + 0x0 + 0x8 = 0x400084 +# ARM-CALL: SYMBOL TABLE: +# ARM-CALL: 00400074 g F .text {{[0-9a-f]+}} _Z1fv +# ARM-CALL: 00400084 g F .text {{[0-9a-f]+}} _Z2f2v +# ARM-CALL: 00400090 g F .text {{[0-9a-f]+}} main + +# Check R_ARM_THM_CALL veneer to call ARM code +# RUN: llvm-objdump -s -t %t | FileCheck -check-prefix=THM-CALL %s + +# THM-CALL: Contents of section .text: +# THM-CALL: 400094 00bffff7 eeef0023 184680bd +# offset = -0x24 ^^ +# call site aligned = Align(0x400096, 4) = 0x400094 +# call site aligned offset PC(thm) _Z1fv addr +# 0x400094 + (-0x24) + 0x4 = 0x400074 +# THM-CALL: SYMBOL TABLE: +# THM-CALL: 00400074 g F .text {{[0-9a-f]+}} _Z1fv +# THM-CALL: 00400084 g F .text {{[0-9a-f]+}} _Z2f2v +# THM-CALL: 00400090 g F .text {{[0-9a-f]+}} main + +# arm.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 00482DE904B08DE2FEFFFFEB0088BDE8 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: .text + Relocations: + - Offset: 0x0000000000000008 + Symbol: _Z2f2v + Type: R_ARM_CALL + Addend: 0 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: _Z1fv + Type: STT_FUNC + Section: .text + Size: 0x0000000000000010 + - Name: _Z2f2v + +# thm.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 80B400AFBD465DF8047B704780B500AF00BFFFF7FEFF0023184680BD + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: .text + Relocations: + - Offset: 0x0000000000000012 + Symbol: _Z1fv + Type: R_ARM_THM_CALL + Addend: 0 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: _Z2f2v + Type: STT_FUNC + Section: .text + Value: 0x0000000000000001 + Size: 0x000000000000000C + - Name: main + Type: STT_FUNC + Section: .text + Value: 0x000000000000000D + Size: 0x0000000000000010 + - Name: _Z1fv +... diff --git a/test/elf/ARM/rel-rel32.test b/test/elf/ARM/rel-rel32.test new file mode 100644 index 0000000000000..47779ac918e37 --- /dev/null +++ b/test/elf/ARM/rel-rel32.test @@ -0,0 +1,57 @@ +# Check handling of R_ARM_REL32 relocation. +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-o.o -o %t +# RUN: llvm-objdump -s -t %t | FileCheck %s + +# CHECK: Contents of section .text: +# CHECK-NEXT: 400074 {{[0-9a-f]+}} 880fff00 +# CHECK: SYMBOL TABLE: +# CHECK: 00400074 g F .text {{[0-9a-f]+}} main +# CHECK: 00401000 g .bss {{[0-9a-f]+}} _myref + +--- +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 80B400AF0000FF0000231846BD465DF8047B7047 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: .text + Relocations: + - Offset: 0x0000000000000004 + Symbol: _myref + Type: R_ARM_REL32 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' +Symbols: + Global: + - Name: _myref + Type: STT_OBJECT + Section: .bss + Size: 0x0000000000000004 + - Name: main + Type: STT_FUNC + Section: .text + Value: 0x0000000000000001 + Size: 0x0000000000000014 +... diff --git a/test/elf/ARM/rel-thm-call.test b/test/elf/ARM/rel-thm-call.test new file mode 100644 index 0000000000000..b9bf8cefc0b81 --- /dev/null +++ b/test/elf/ARM/rel-thm-call.test @@ -0,0 +1,61 @@ +# Check handling of R_ARM_THM_CALL relocation. +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-o.o -o %t +# RUN: llvm-objdump -s -t %t | FileCheck %s + +# CHECK: Contents of section .text: +# CHECK: 400084 fff7f6ff 00231846 80bd00bf +# ^^ offset = -0x14 +# call site offset PC(thm) _Z1fv addr +# 0x400084 + (-0x14) + 0x4 = 0x400074 +# CHECK: SYMBOL TABLE: +# CHECK: 00400074 g F .text {{[0-9a-f]+}} _Z1fv +# CHECK: 00400080 g F .text {{[0-9a-f]+}} main + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 80B400AFBD465DF8047B704780B500AFFFF7FEFF0023184680BD00BF + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: .text + Relocations: + - Offset: 0x0000000000000010 + Symbol: _Z1fv + Type: R_ARM_THM_CALL + Addend: 0 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: _Z1fv + Type: STT_FUNC + Section: .text + Value: 0x0000000000000001 + Size: 0x000000000000000C + - Name: main + Type: STT_FUNC + Section: .text + Value: 0x000000000000000D + Size: 0x000000000000000E +... diff --git a/test/elf/ARM/rel-thm-jump11.test b/test/elf/ARM/rel-thm-jump11.test new file mode 100644 index 0000000000000..4998c2b96c975 --- /dev/null +++ b/test/elf/ARM/rel-thm-jump11.test @@ -0,0 +1,141 @@ +# Check handling of R_ARM_THM_JUMP11 relocation. +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-o.o -o %t +# RUN: llvm-objdump -s -t %t | FileCheck %s + +# CHECK: Contents of section .text: +# CHECK: 4001a4 0021c7e7 +# CHECK: SYMBOL TABLE: +# CHECK: 00400138 g F .text 00000060 __gnu_h2f_internal +# CHECK: 004001a4 g F .text 00000004 __gnu_h2f_alternative + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 70B4020CC0F3C754FF2CC0F3160302F4004080B241D0002C08BF002B3BD0A4F17F0543F4000315F10E0FA8BF41F6FF7209DA15F1190FA3BFA4F166066FF07F42F2406FF07F4212EA03060CD001325208964208BF03EA42021344B3F1807F24BF5B08A4F17E0501B30F2D26DC15F1180F11DB15F10E0FB5BF4FF6F2710E35CFF6FF71AD02B7BF491BAAB2CB40002202EB5333034398B270BC70470029FBD040EA533370BC43F4FC407047102DDEDD6FEAD0336FEAC33398B2EDE740F4F84398B2E9E700BFC0F3842310B4A3F11F040029B4FA84F400F400424FEA541408BF0024C0F309002146140481B943B14203703302EBC35343EA04005DF8044B704760B1B0FA80F3153B98405B42EFE744EA40305DF8044B40F0FF40704720465DF8044B704700BF01216BE70121FEE7002167E70021FEE7 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: .text + Relocations: + - Offset: 0x000000000000012A + Symbol: __gnu_h2f_internal + Type: R_ARM_THM_JUMP11 + - Offset: 0x0000000000000132 + Symbol: __gnu_h2f_internal + Type: R_ARM_THM_JUMP11 + - Name: .text.startup + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 0020FFF7FEBF00BF + - Name: .rel.text.startup + Type: SHT_REL + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: .text.startup + Relocations: + - Offset: 0x0000000000000002 + Symbol: __gnu_h2f_alternative + Type: R_ARM_THM_JUMP24 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 004743433A202863726F7373746F6F6C2D4E47206C696E61726F2D312E31332E312D342E392D323031342E3039202D204C696E61726F2047434320342E392D323031342E30392920342E392E32203230313430393034202870726572656C656173652900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .ARM.attributes + Type: SHT_ARM_ATTRIBUTES + AddressAlign: 0x0000000000000001 + Content: 4134000000616561626900012A00000005372D4100060A0741080109020A041204140115011703180119011A021B031C011E022201 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .text.startup + Type: STT_SECTION + Section: .text.startup + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .comment + Type: STT_SECTION + Section: .comment + - Name: .note.GNU-stack + Type: STT_SECTION + Section: .note.GNU-stack + - Name: .ARM.attributes + Type: STT_SECTION + Section: .ARM.attributes + - Name: '$t' + Section: .text + - Name: __gnu_f2h_internal + Type: STT_FUNC + Section: .text + Value: 0x0000000000000001 + Size: 0x00000000000000C2 + Global: + - Name: __gnu_f2h_alternative + Type: STT_FUNC + Section: .text + Value: 0x000000000000012D + Size: 0x0000000000000004 + Visibility: STV_HIDDEN + - Name: __gnu_h2f_alternative + Type: STT_FUNC + Section: .text + Value: 0x0000000000000131 + Size: 0x0000000000000004 + Visibility: STV_HIDDEN + - Name: __gnu_h2f_ieee + Type: STT_FUNC + Section: .text + Value: 0x0000000000000129 + Size: 0x0000000000000004 + Visibility: STV_HIDDEN + - Name: main + Type: STT_FUNC + Section: .text.startup + Value: 0x0000000000000001 + Size: 0x0000000000000006 + - Name: __gnu_f2h_ieee + Type: STT_FUNC + Section: .text + Value: 0x0000000000000125 + Size: 0x0000000000000004 + Visibility: STV_HIDDEN + - Name: __gnu_h2f_internal + Type: STT_FUNC + Section: .text + Value: 0x00000000000000C5 + Size: 0x000000000000005E + Visibility: STV_HIDDEN +... diff --git a/test/elf/ARM/rel-thm-jump24-veneer.test b/test/elf/ARM/rel-thm-jump24-veneer.test new file mode 100644 index 0000000000000..fffce44ca477f --- /dev/null +++ b/test/elf/ARM/rel-thm-jump24-veneer.test @@ -0,0 +1,100 @@ +# Check veneer generation for R_ARM_THM_JUMP24 relocation (B instruction call). + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-arm.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-thm.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-arm.o %t-thm.o -o %t + +# RUN: llvm-objdump -s -t %t | FileCheck -check-prefix=B-VENEER %s +# RUN: llvm-objdump -s -t %t | FileCheck -check-prefix=B-ADDR %s + +# B-VENEER: Contents of section .text: +# B-VENEER: 400074 {{[0-9a-f]+}} {{[0-9a-f]+}} {{[0-9a-f]+}} 00f000b8 +# Call from main: +# offset = 0x0 ^^ +# call site offset PC(thm) ___Z1fv_from_thumb addr +# 0x400080 + 0x0 + 0x4 = 0x400084 +# +# Code of the veneer: +# B-VENEER: 400084 78470000 f9ffffea +# offset = -0x1C ^^ +# call site offset PC(arm) _Z1fv +# 0x400088 + (-0x1C) + 0x8 = 0x400074 +# +# B-ADDR: SYMBOL TABLE: +# B-ADDR: 00400084 l F .text {{[0-9a-f]+}} ___Z1fv_from_thumb +# B-ADDR: 00400074 g F .text {{[0-9a-f]+}} _Z1fv +# B-ADDR: 00400080 g F .text {{[0-9a-f]+}} main + +# arm.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 0030A0E30300A0E11EFF2FE1 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: _Z1fv + Type: STT_FUNC + Section: .text + +# thm.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: FFF7FEBF + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: .text + Relocations: + - Offset: 0x0000000000000000 + Symbol: _Z1fv + Type: R_ARM_THM_JUMP24 + Addend: 0 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: main + Type: STT_FUNC + Section: .text + Value: 0x0000000000000001 + - Name: _Z1fv +... diff --git a/test/elf/ARM/rel-thm-jump24.test b/test/elf/ARM/rel-thm-jump24.test new file mode 100644 index 0000000000000..6c9b63447c9d9 --- /dev/null +++ b/test/elf/ARM/rel-thm-jump24.test @@ -0,0 +1,59 @@ +# Check handling of R_ARM_THM_JUMP24 relocation. +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-o.o -o %t +# RUN: llvm-objdump -s -t %t | FileCheck %s + +# CHECK: Contents of section .text: +# CHECK: 400084 80b500af fff7f4bf 03461846 80bd00bf +# ^^ offset = -0x18 +# call site offset PC(thm) _Z1fv addr +# 0x400088 + (-0x18) + 0x4 = 0x400074 +# CHECK: SYMBOL TABLE: +# CHECK: 00400074 g F .text {{[0-9a-f]+}} _Z1fv +# CHECK: 00400084 g F .text {{[0-9a-f]+}} main + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 80B400AF00231846BD465DF8047B704780B500AFFFF7FEBF0346184680BD00BF + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: .text + Relocations: + - Offset: 0x0000000000000014 + Symbol: _Z1fv + Type: R_ARM_THM_JUMP24 + Addend: 0 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: _Z1fv + Type: STT_FUNC + Section: .text + Value: 0x0000000000000001 + - Name: main + Type: STT_FUNC + Section: .text + Value: 0x0000000000000011 +... diff --git a/test/elf/ARM/rel-thm-mov.test b/test/elf/ARM/rel-thm-mov.test new file mode 100644 index 0000000000000..25edc4cda4b9c --- /dev/null +++ b/test/elf/ARM/rel-thm-mov.test @@ -0,0 +1,70 @@ +# 1. Check handling of R_ARM_THM_MOVW_ABS_NC and R_THM_ARM_MOVT_ABS relocation pair. +# 2. Check that instructions are not cropped for symbols that address Thumb code. +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-o.o -o %t +# RUN: llvm-objdump -s -t %t | FileCheck %s +# RUN: llvm-objdump -s -t %t | FileCheck -check-prefix=INSN-CROP %s + +# CHECK: Contents of section .text: +# CHECK: 400074 {{[0-9a-f]+}} 41f20003 c0f24003 0a221a60 +# addrL = 0x1000 ^^ +# addrH = 0x40 ^^ +# addrH addrL _ZL5data1 addr +# (0x40 << 16) + 0x1000 = 0x401000 +# CHECK: SYMBOL TABLE: +# CHECK: 00401000 l .bss 00000004 _ZL5data1 +# CHECK: 00400074 g F .text {{[0-9a-f]+}} main +# +# INSN-CROP: Contents of section .text: +# INSN-CROP: 400074 80b400af + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 80B400AF40F20003C0F200030A221A6000231846BD465DF8047B7047 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: .text + Relocations: + - Offset: 0x0000000000000004 + Symbol: _ZL5data1 + Type: R_ARM_THM_MOVW_ABS_NC + Addend: 0 + - Offset: 0x0000000000000008 + Symbol: _ZL5data1 + Type: R_ARM_THM_MOVT_ABS + Addend: 0 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: 411C0000 +Symbols: + Local: + - Name: _ZL5data1 + Type: STT_OBJECT + Section: .bss + Size: 0x0000000000000004 + Global: + - Name: main + Type: STT_FUNC + Section: .text + Value: 0x0000000000000001 +... diff --git a/test/elf/ARM/rel-tls-ie32.test b/test/elf/ARM/rel-tls-ie32.test new file mode 100644 index 0000000000000..7b65c2f194c7e --- /dev/null +++ b/test/elf/ARM/rel-tls-ie32.test @@ -0,0 +1,109 @@ +# Check handling of R_ARM_TLS_IE32 relocation. +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-tls.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-tlsv.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-tls.o %t-tlsv.o -o %t +# RUN: llvm-objdump -s -t %t | FileCheck %s + +# CHECK: Contents of section .got: +# CHECK: 401008 08000000 0c000000 +# tp_off(i) = 0x08 ^^ ^^ tp_off(j) = 0x0c +# tp_off(i) + sizeof(i) = tp_off(j) +# 0x08 + 0x04 = 0x0c +# CHECK: SYMBOL TABLE: +# CHECK: 00400094 g F .text {{[0-9a-f]+}} main +# CHECK: 00000000 g .tdata 00000004 i +# sizeof(i) = 0x04 ^^ +# CHECK: 00000004 g .tdata 00000004 j + +# tls.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 80B400AF0C4B7B441B681DEE702FD2580A4B7B441B681DEE701FCB581A44084B7B441B681DEE701FCB585B0013441846BD465DF8047B70472E000000260000001C000000 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: .text + Relocations: + - Offset: 0x0000000000000038 + Symbol: i + Type: R_ARM_TLS_IE32 + - Offset: 0x000000000000003C + Symbol: i + Type: R_ARM_TLS_IE32 + - Offset: 0x0000000000000040 + Symbol: j + Type: R_ARM_TLS_IE32 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: main + Type: STT_FUNC + Section: .text + Value: 0x0000000000000001 + - Name: i + Type: STT_TLS + - Name: j + Type: STT_TLS + +# tlsv.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 0x0000000000000004 + Content: 05000000FBFFFFFF +Symbols: + Global: + - Name: i + Type: STT_TLS + Section: .tdata + Size: 0x0000000000000004 + - Name: j + Type: STT_TLS + Section: .tdata + Value: 0x0000000000000004 + Size: 0x0000000000000004 +... diff --git a/test/elf/ARM/rel-tls-le32.test b/test/elf/ARM/rel-tls-le32.test new file mode 100644 index 0000000000000..d0d3d5b6821af --- /dev/null +++ b/test/elf/ARM/rel-tls-le32.test @@ -0,0 +1,61 @@ +# Check handling of R_ARM_TLS_LE32 relocation. +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-o.o -o %t +# RUN: llvm-objdump -s -t %t | FileCheck %s + +# CHECK: Contents of section .text: +# CHECK: 4000b4 {{[0-9a-f]+}} 08000000 +# tp_off = 0x000008 ^^ +# CHECK: SYMBOL TABLE: +# CHECK: 00400094 g F .text {{[0-9a-f]+}} main +# CHECK: 00000000 g .tdata 00000004 i + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 04B02DE500B08DE2703F1DEE10209FE5023093E70300A0E100D04BE204B09DE41EFF2FE100000000 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: .text + Relocations: + - Offset: 0x0000000000000024 + Symbol: i + Type: R_ARM_TLS_LE32 + Addend: 0 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 0x0000000000000004 + Content: '05000000' +Symbols: + Global: + - Name: i + Type: STT_TLS + Section: .tdata + Size: 0x0000000000000004 + - Name: main + Type: STT_FUNC + Section: .text +... diff --git a/test/elf/ARM/thm-symbols.test b/test/elf/ARM/thm-symbols.test new file mode 100644 index 0000000000000..c91505c98100d --- /dev/null +++ b/test/elf/ARM/thm-symbols.test @@ -0,0 +1,52 @@ +# Check that symbols formed from Thumb instructions are valid: +# 1. Symbol address. +# 2. Symbol content size. +# 3. Symbol content. + +# RUN: yaml2obj -format=elf %s > %t-t.o +# RUN: lld -flavor gnu -target arm-linux-gnu \ +# RUN: -Bstatic --noinhibit-exec %t-t.o -o %t-t +# RUN: llvm-readobj -symbols %t-t | FileCheck -check-prefix=SYM-ADDR %s +# RUN: llvm-readobj -symbols %t-t | FileCheck -check-prefix=SYM-SIZE %s +# RUN: llvm-objdump -s -t %t-t | FileCheck -check-prefix=SYM-CONTENT %s + +# SYM-ADDR: Name: main (1) +# SYM-ADDR-NEXT: Value: 0x400075 + +# SYM-SIZE: Name: main (1) +# SYM-SIZE-NEXT: Value: 0x{{[0-9a-f]+}} +# SYM-SIZE-NEXT: Size: 16 + +# SYM-CONTENT: Contents of section .text: +# SYM-CONTENT-NEXT: 400074 80b400af 00231846 bd465df8 047b7047 .....#.F.F]..{pG + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 80B400AF00231846BD465DF8047B7047 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: main + Type: STT_FUNC + Section: .text + Value: 0x0000000000000001 +... diff --git a/test/elf/ARM/undef-lazy-symbol.test b/test/elf/ARM/undef-lazy-symbol.test new file mode 100644 index 0000000000000..f32549e85be45 --- /dev/null +++ b/test/elf/ARM/undef-lazy-symbol.test @@ -0,0 +1,135 @@ +# Check that _GLOBAL_OFFSET_TABLE_ symbol is resolved + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-got.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-got.o -o %t +# RUN: llvm-readobj -symbols %t | FileCheck -check-prefix=GOT %s + +# GOT: Name: _GLOBAL_OFFSET_TABLE_ (185) +# GOT-NEXT: Value: {{[0-9]+}} +# GOT-NEXT: Size: 0 +# GOT-NEXT: Binding: Global (0x1) +# GOT-NEXT: Type: Object (0x1) +# GOT-NEXT: Other: 0 +# GOT-NEXT: Section: Absolute (0xFFF1) + +# Check that __exidx_start/_end symbols are resolved + +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-exidx.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --defsym=main=fn --noinhibit-exec %t-exidx.o -o %t +# RUN: llvm-readobj -symbols %t | FileCheck -check-prefix=EXIDX %s + +# EXIDX: Name: __exidx_start (188) +# EXIDX-NEXT: Value: {{[0-9]+}} +# EXIDX-NEXT: Size: 0 +# EXIDX-NEXT: Binding: Global (0x1) +# EXIDX-NEXT: Type: Object (0x1) +# EXIDX-NEXT: Other: 0 +# EXIDX-NEXT: Section: Absolute (0xFFF1) +# +# EXIDX: Name: __exidx_end (202) +# EXIDX-NEXT: Value: {{[0-9]+}} +# EXIDX-NEXT: Size: 0 +# EXIDX-NEXT: Binding: Global (0x1) +# EXIDX-NEXT: Type: Object (0x1) +# EXIDX-NEXT: Other: 0 +# EXIDX-NEXT: Section: Absolute (0xFFF1) + +# Check that all symbols are resolved + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-got.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-exidx.o +# RUN: lld -flavor gnu -target arm -m armelf_linux_eabi -Bstatic \ +# RUN: --noinhibit-exec %t-got.o %t-exidx.o -o %t +# RUN: llvm-readobj -symbols %t | FileCheck -check-prefix=SYMS %s + +# SYMS: Name: _GLOBAL_OFFSET_TABLE_ (188) +# SYMS-NEXT: Value: {{[0-9]+}} +# SYMS-NEXT: Size: 0 +# SYMS-NEXT: Binding: Global (0x1) +# SYMS-NEXT: Type: Object (0x1) +# SYMS-NEXT: Other: 0 +# SYMS-NEXT: Section: Absolute (0xFFF1) +# +# SYMS: Name: __exidx_start (210) +# SYMS-NEXT: Value: {{[0-9]+}} +# SYMS-NEXT: Size: 0 +# SYMS-NEXT: Binding: Global (0x1) +# SYMS-NEXT: Type: Object (0x1) +# SYMS-NEXT: Other: 0 +# SYMS-NEXT: Section: Absolute (0xFFF1) +# +# SYMS: Name: __exidx_end (224) +# SYMS-NEXT: Value: {{[0-9]+}} +# SYMS-NEXT: Size: 0 +# SYMS-NEXT: Binding: Global (0x1) +# SYMS-NEXT: Type: Object (0x1) +# SYMS-NEXT: Other: 0 +# SYMS-NEXT: Section: Absolute (0xFFF1) + +# got.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 80B483B000AF40F20003C0F200037B60002318460C37BD465DF8047B704700BF + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: main + Type: STT_FUNC + Section: .text + Value: 0x0000000000000001 + - Name: _GLOBAL_OFFSET_TABLE_ + +# exidx.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_ARM + Flags: [ EF_ARM_EABI_VER5 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 80B483B000AF40F20003C0F200037B60002318460C37BD465DF8047B704700BF + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Global: + - Name: fn + Type: STT_FUNC + Section: .text + Value: 0x0000000000000001 + - Name: __exidx_start + - Name: __exidx_end +... diff --git a/test/elf/Hexagon/Inputs/dynobj-data.c b/test/elf/Hexagon/Inputs/dynobj-data.c new file mode 100644 index 0000000000000..0f4ea9b80526c --- /dev/null +++ b/test/elf/Hexagon/Inputs/dynobj-data.c @@ -0,0 +1,3 @@ +int d = 10; + +int fn() { return d; } diff --git a/test/elf/Hexagon/Inputs/dynobj-data.o b/test/elf/Hexagon/Inputs/dynobj-data.o Binary files differnew file mode 100644 index 0000000000000..13d283cd0eff6 --- /dev/null +++ b/test/elf/Hexagon/Inputs/dynobj-data.o diff --git a/test/elf/Hexagon/Inputs/dynobj.c b/test/elf/Hexagon/Inputs/dynobj.c new file mode 100644 index 0000000000000..f17fdadd69455 --- /dev/null +++ b/test/elf/Hexagon/Inputs/dynobj.c @@ -0,0 +1,26 @@ +extern int shankar; +static int a; +static int b; +int c; +int fn2() { + return 0; +} + +int fn1() { + return 0; +} + +int fn() { + a = 10; + b = 20; + c = 10; + shankar = 20; + return 0; +} + +int fn3() { + fn(); + fn1(); + fn2(); + return 0; +} diff --git a/test/elf/Hexagon/Inputs/dynobj.o b/test/elf/Hexagon/Inputs/dynobj.o Binary files differnew file mode 100644 index 0000000000000..6c184f2edd605 --- /dev/null +++ b/test/elf/Hexagon/Inputs/dynobj.o diff --git a/test/elf/Hexagon/Inputs/got-plt-order.c b/test/elf/Hexagon/Inputs/got-plt-order.c new file mode 100644 index 0000000000000..621f670ef7c57 --- /dev/null +++ b/test/elf/Hexagon/Inputs/got-plt-order.c @@ -0,0 +1,6 @@ +int c = 10; +int fn() { c = 20; return 0; } + +int fn1() { + return fn(); +} diff --git a/test/elf/Hexagon/Inputs/got-plt-order.o b/test/elf/Hexagon/Inputs/got-plt-order.o Binary files differnew file mode 100644 index 0000000000000..e97678b739bd3 --- /dev/null +++ b/test/elf/Hexagon/Inputs/got-plt-order.o diff --git a/test/elf/Hexagon/Inputs/libMaxAlignment.a b/test/elf/Hexagon/Inputs/libMaxAlignment.a Binary files differnew file mode 100644 index 0000000000000..cc5461a09ae4d --- /dev/null +++ b/test/elf/Hexagon/Inputs/libMaxAlignment.a diff --git a/test/elf/Hexagon/Inputs/sda-base.o b/test/elf/Hexagon/Inputs/sda-base.o Binary files differnew file mode 100644 index 0000000000000..410a3d47d3201 --- /dev/null +++ b/test/elf/Hexagon/Inputs/sda-base.o diff --git a/test/elf/Hexagon/Inputs/sdata1.c b/test/elf/Hexagon/Inputs/sdata1.c new file mode 100644 index 0000000000000..77c2a54c36a1b --- /dev/null +++ b/test/elf/Hexagon/Inputs/sdata1.c @@ -0,0 +1,3 @@ +static int a = 0; + +int b = 10; diff --git a/test/elf/Hexagon/Inputs/sdata1.o b/test/elf/Hexagon/Inputs/sdata1.o Binary files differnew file mode 100644 index 0000000000000..bf908fed27e96 --- /dev/null +++ b/test/elf/Hexagon/Inputs/sdata1.o diff --git a/test/elf/Hexagon/Inputs/sdata2.c b/test/elf/Hexagon/Inputs/sdata2.c new file mode 100644 index 0000000000000..96b37271d7738 --- /dev/null +++ b/test/elf/Hexagon/Inputs/sdata2.c @@ -0,0 +1,6 @@ +int sdata1 = 10; +int sdata2 = 20; +int sdata3 = 30; + +int sbss1 = 0; + diff --git a/test/elf/Hexagon/Inputs/sdata2.o b/test/elf/Hexagon/Inputs/sdata2.o Binary files differnew file mode 100644 index 0000000000000..e4ec810866e51 --- /dev/null +++ b/test/elf/Hexagon/Inputs/sdata2.o diff --git a/test/elf/Hexagon/Inputs/use-shared.hexagon b/test/elf/Hexagon/Inputs/use-shared.hexagon Binary files differnew file mode 100644 index 0000000000000..9e5ffb42d36ad --- /dev/null +++ b/test/elf/Hexagon/Inputs/use-shared.hexagon diff --git a/test/elf/Hexagon/dynlib-data.test b/test/elf/Hexagon/dynlib-data.test new file mode 100644 index 0000000000000..f3260ba4847bf --- /dev/null +++ b/test/elf/Hexagon/dynlib-data.test @@ -0,0 +1,9 @@ +RUN: lld -flavor gnu -target hexagon %p/Inputs/dynobj-data.o \ +RUN: -o %t --noinhibit-exec -shared +RUN: llvm-objdump -s %t > %t1 +RUN: FileCheck -check-prefix=CHECKRELOCS %s < %t1 + +CHECKRELOCS: Contents of section .text: +CHECKRELOCS: 00f8 01c09da0 01d89da1 3c400000 18c4496a ........<@....Ij +CHECKRELOCS: 0108 ff7fff0f 00ff9897 00c08091 38c09d91 ............8... +CHECKRELOCS: 0118 1ec01e96 .... diff --git a/test/elf/Hexagon/dynlib-gotoff.test b/test/elf/Hexagon/dynlib-gotoff.test new file mode 100644 index 0000000000000..752d3acd4c082 --- /dev/null +++ b/test/elf/Hexagon/dynlib-gotoff.test @@ -0,0 +1,128 @@ +# This tests GOT's and PLT's for dynamic libraries for Hexagon +RUN: lld -flavor gnu -target hexagon %p/Inputs/dynobj.o \ +RUN: -o %t --output-filetype=yaml -shared --noinhibit-exec +RUN: FileCheck -check-prefix=CHECKGOTPLT %s < %t + + - name: .PLT0 +CHECKGOTPLT: type: stub +CHECKGOTPLT: content: [ 00, 40, 00, 00, 1C, C0, 49, 6A, 0E, 42, 9C, E2, +CHECKGOTPLT: 4F, 40, 9C, 91, 3C, C0, 9C, 91, 0E, 42, 0E, 8C, +CHECKGOTPLT: 00, C0, 9C, 52 ] +CHECKGOTPLT: alignment: 2^4 +CHECKGOTPLT: section-name: .plt +CHECKGOTPLT: references: +CHECKGOTPLT: - kind: R_HEX_B32_PCREL_X +CHECKGOTPLT: offset: 0 + target: __got0 +CHECKGOTPLT: - kind: R_HEX_6_PCREL_X +CHECKGOTPLT: offset: 4 + target: __got0 +CHECKGOTPLT: addend: 4 + - name: __plt_fn +CHECKGOTPLT: type: stub +CHECKGOTPLT: content: [ 00, 40, 00, 00, 0E, C0, 49, 6A, 1C, C0, 8E, 91, +CHECKGOTPLT: 00, C0, 9C, 52 ] +CHECKGOTPLT: alignment: 2^4 +CHECKGOTPLT: section-name: .plt +CHECKGOTPLT: references: +CHECKGOTPLT: - kind: R_HEX_B32_PCREL_X +CHECKGOTPLT: offset: 0 + target: __got_fn +CHECKGOTPLT: - kind: R_HEX_6_PCREL_X +CHECKGOTPLT: offset: 4 + target: __got_fn +CHECKGOTPLT: addend: 4 + - name: __plt_fn1 +CHECKGOTPLT: type: stub +CHECKGOTPLT: content: [ 00, 40, 00, 00, 0E, C0, 49, 6A, 1C, C0, 8E, 91, +CHECKGOTPLT: 00, C0, 9C, 52 ] +CHECKGOTPLT: alignment: 2^4 +CHECKGOTPLT: section-name: .plt +CHECKGOTPLT: references: +CHECKGOTPLT: - kind: R_HEX_B32_PCREL_X +CHECKGOTPLT: offset: 0 + target: __got_fn1 +CHECKGOTPLT: - kind: R_HEX_6_PCREL_X +CHECKGOTPLT: offset: 4 + target: __got_fn1 +CHECKGOTPLT: addend: 4 + - name: __plt_fn2 +CHECKGOTPLT: type: stub +CHECKGOTPLT: content: [ 00, 40, 00, 00, 0E, C0, 49, 6A, 1C, C0, 8E, 91, +CHECKGOTPLT: 00, C0, 9C, 52 ] +CHECKGOTPLT: alignment: 2^4 +CHECKGOTPLT: section-name: .plt +CHECKGOTPLT: references: +CHECKGOTPLT: - kind: R_HEX_B32_PCREL_X +CHECKGOTPLT: offset: 0 + target: __got_fn2 +CHECKGOTPLT: - kind: R_HEX_6_PCREL_X +CHECKGOTPLT: offset: 4 + target: __got_fn2 +CHECKGOTPLT: addend: 4 + - name: __got0 +CHECKGOTPLT: type: got +CHECKGOTPLT: content: [ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, +CHECKGOTPLT: 00, 00, 00, 00 ] +CHECKGOTPLT: alignment: 2^3 +CHECKGOTPLT: section-name: .got.plt +CHECKGOTPLT: permissions: rw- + - name: __got_c +CHECKGOTPLT: type: got +CHECKGOTPLT: content: [ 00, 00, 00, 00 ] +CHECKGOTPLT: alignment: 2^2 +CHECKGOTPLT: section-name: .got +CHECKGOTPLT: permissions: rw- +CHECKGOTPLT: references: +CHECKGOTPLT: - kind: R_HEX_GLOB_DAT +CHECKGOTPLT: offset: 0 +CHECKGOTPLT: target: c + - name: __got_shankar +CHECKGOTPLT: type: got +CHECKGOTPLT: content: [ 00, 00, 00, 00 ] +CHECKGOTPLT: alignment: 2^2 +CHECKGOTPLT: section-name: .got +CHECKGOTPLT: permissions: rw- +CHECKGOTPLT: references: +CHECKGOTPLT: - kind: R_HEX_GLOB_DAT +CHECKGOTPLT: offset: 0 +CHECKGOTPLT: target: shankar + - name: __got_fn +CHECKGOTPLT: type: got +CHECKGOTPLT: content: [ 00, 00, 00, 00 ] +CHECKGOTPLT: alignment: 2^2 +CHECKGOTPLT: section-name: .got.plt +CHECKGOTPLT: permissions: rw- +CHECKGOTPLT: references: +CHECKGOTPLT: - kind: R_HEX_JMP_SLOT +CHECKGOTPLT: offset: 0 +CHECKGOTPLT: target: fn +CHECKGOTPLT: - kind: R_HEX_32 +CHECKGOTPLT: offset: 0 + target: .PLT0 + - name: __got_fn1 +CHECKGOTPLT: type: got +CHECKGOTPLT: content: [ 00, 00, 00, 00 ] +CHECKGOTPLT: alignment: 2^2 +CHECKGOTPLT: section-name: .got.plt +CHECKGOTPLT: permissions: rw- +CHECKGOTPLT: references: +CHECKGOTPLT: - kind: R_HEX_JMP_SLOT +CHECKGOTPLT: offset: 0 +CHECKGOTPLT: target: fn1 +CHECKGOTPLT: - kind: R_HEX_32 +CHECKGOTPLT: offset: 0 + target: .PLT0 + - name: __got_fn2 +CHECKGOTPLT: type: got +CHECKGOTPLT: content: [ 00, 00, 00, 00 ] +CHECKGOTPLT: alignment: 2^2 +CHECKGOTPLT: section-name: .got.plt +CHECKGOTPLT: permissions: rw- +CHECKGOTPLT: references: +CHECKGOTPLT: - kind: R_HEX_JMP_SLOT +CHECKGOTPLT: offset: 0 +CHECKGOTPLT: target: fn2 +CHECKGOTPLT: - kind: R_HEX_32 +CHECKGOTPLT: offset: 0 + target: .PLT0 diff --git a/test/elf/Hexagon/dynlib-hash.test b/test/elf/Hexagon/dynlib-hash.test new file mode 100644 index 0000000000000..f931764923485 --- /dev/null +++ b/test/elf/Hexagon/dynlib-hash.test @@ -0,0 +1,9 @@ +RUN: lld -flavor gnu -target hexagon %p/Inputs/dynobj.o \ +RUN: -o %t --noinhibit-exec -shared +RUN: llvm-objdump -s %t > %t1 +RUN: FileCheck -check-prefix=CHECKHASH %s < %t1 + +CHECKHASH: Contents of section .hash: +CHECKHASH: 0094 03000000 07000000 06000000 01000000 +CHECKHASH: 00a4 04000000 00000000 00000000 00000000 +CHECKHASH: 00b4 00000000 03000000 02000000 05000000 diff --git a/test/elf/Hexagon/dynlib-rela.test b/test/elf/Hexagon/dynlib-rela.test new file mode 100644 index 0000000000000..81617349e2f74 --- /dev/null +++ b/test/elf/Hexagon/dynlib-rela.test @@ -0,0 +1,9 @@ +# Tests that the relocation sections have the right alignment. +RUN: lld -flavor gnu -target hexagon %p/Inputs/use-shared.hexagon -shared -o %t1 +RUN: llvm-readobj -sections %t1 > %t2 +RUN: FileCheck -check-prefix=SECTIONS %s < %t2 + +SECTIONS: Section { +SECTIONS: Name: .rela.plt (23) +SECTIONS: AddressAlignment: 4 +SECTIONS: } diff --git a/test/elf/Hexagon/dynlib-syms.test b/test/elf/Hexagon/dynlib-syms.test new file mode 100644 index 0000000000000..e649230d9e907 --- /dev/null +++ b/test/elf/Hexagon/dynlib-syms.test @@ -0,0 +1,7 @@ +RUN: lld -flavor gnu -target hexagon %p/Inputs/dynobj.o \ +RUN: -o %t --noinhibit-exec -shared +RUN: llvm-nm -n -M %t > %t1 +RUN: FileCheck -check-prefix=CHECKSYMS %s < %t1 + +CHECKSYMS: 0000025c A _DYNAMIC +CHECKSYMS: 00001008 A _GLOBAL_OFFSET_TABLE_ diff --git a/test/elf/Hexagon/dynlib.test b/test/elf/Hexagon/dynlib.test new file mode 100644 index 0000000000000..c5bf23fdf2295 --- /dev/null +++ b/test/elf/Hexagon/dynlib.test @@ -0,0 +1,36 @@ +RUN: lld -flavor gnu -target hexagon %p/Inputs/use-shared.hexagon -shared -o %t1 +RUN: llvm-readobj -dyn-symbols %t1 > %t2 +RUN: FileCheck -check-prefix=DYNSYMS %s < %t2 +RUN: llvm-readobj -program-headers %t1 | FileCheck %s + +DYNSYMS: DynamicSymbols [ +DYNSYMS: Symbol { +DYNSYMS: Name: fn2 +DYNSYMS-NEXT: Value: +DYNSYMS-NEXT: Size: +DYNSYMS-NEXT: Binding: Global +DYNSYMS-NEXT: Type: Function +DYNSYMS-NEXT: Other: +DYNSYMS-NEXT: Section: .text +DYNSYMS: } +DYNSYMS: Symbol { +DYNSYMS: Name: fn1 +DYNSYMS-NEXT: Value: +DYNSYMS-NEXT: Size: +DYNSYMS-NEXT: Binding: Global +DYNSYMS-NEXT: Type: Function +DYNSYMS-NEXT: Other: +DYNSYMS-NEXT: Section: .text +DYNSYMS: } +DYNSYMS: Symbol { +DYNSYMS: Name: fn3 +DYNSYMS-NEXT: Value: +DYNSYMS-NEXT: Size: +DYNSYMS-NEXT: Binding: Global +DYNSYMS-NEXT: Type: Function +DYNSYMS-NEXT: Other: +DYNSYMS-NEXT: Section: .text +DYNSYMS-NEXT: } +DYNSYMS-NEXT: ] + +CHECK-NOT: PT_PHDR diff --git a/test/elf/Hexagon/hexagon-got-plt-order.test b/test/elf/Hexagon/hexagon-got-plt-order.test new file mode 100644 index 0000000000000..7600ebe59fc59 --- /dev/null +++ b/test/elf/Hexagon/hexagon-got-plt-order.test @@ -0,0 +1,5 @@ +RUN: lld -flavor gnu -target hexagon %p/Inputs/got-plt-order.o -o %t -shared +RUN: llvm-objdump -section-headers %t | FileCheck %s + +CHECK: .got +CHECK-NEXT: .got.plt diff --git a/test/elf/Hexagon/hexagon-plt-setup.test b/test/elf/Hexagon/hexagon-plt-setup.test new file mode 100644 index 0000000000000..15c4e22ed9c53 --- /dev/null +++ b/test/elf/Hexagon/hexagon-plt-setup.test @@ -0,0 +1,12 @@ +RUN: lld -flavor gnu -target hexagon %p/Inputs/use-shared.hexagon \ +RUN: --output-filetype=yaml --noinhibit-exec -o %t2 +RUN: FileCheck %s < %t2 + +CHECK: - name: fn3 +CHECK: references: +CHECK: - kind: R_HEX_B22_PCREL +CHECK: offset: 4 + target: +CHECK: - kind: R_HEX_B22_PCREL +CHECK: offset: 8 + target: diff --git a/test/elf/Hexagon/maxalignment.test b/test/elf/Hexagon/maxalignment.test new file mode 100644 index 0000000000000..cac1c200734fa --- /dev/null +++ b/test/elf/Hexagon/maxalignment.test @@ -0,0 +1,8 @@ +# This tests that we lld is able to get the contentType properly for archives +# when they intermittently get loaded at an address whose alignment is 2 + +RUN: lld -flavor gnu -target hexagon --whole-archive %p/Inputs/libMaxAlignment.a \ +RUN: --noinhibit-exec -static -o %t +RUN: llvm-nm %t | FileCheck %s + +CHECK: {{[0-9a-f]+}} D a diff --git a/test/elf/Hexagon/rela-order.test b/test/elf/Hexagon/rela-order.test new file mode 100644 index 0000000000000..925a82c292900 --- /dev/null +++ b/test/elf/Hexagon/rela-order.test @@ -0,0 +1,9 @@ +RUN: lld -flavor gnu -target hexagon %p/Inputs/dynobj.o -shared \ +RUN: --noinhibit-exec -o %t +RUN: llvm-objdump -section-headers %t | FileCheck %s + +CHECK: .dynsym +CHECK-NEXT: .dynstr +CHECK-NEXT: .rela.dyn +CHECK-NEXT: .rela.plt +CHECK-NEXT: .plt diff --git a/test/elf/Hexagon/sda-base.test b/test/elf/Hexagon/sda-base.test new file mode 100644 index 0000000000000..0bab92abf6852 --- /dev/null +++ b/test/elf/Hexagon/sda-base.test @@ -0,0 +1,4 @@ +RUN: lld -flavor gnu -target hexagon %p/Inputs/sda-base.o -o %t1 --noinhibit-exec +RUN: llvm-nm -n %t1 | FileCheck %s -check-prefix=sdabase + +sdabase: 00002000 A _SDA_BASE_ diff --git a/test/elf/Hexagon/zerofillquick-sdata.test b/test/elf/Hexagon/zerofillquick-sdata.test new file mode 100644 index 0000000000000..5488e19f5c445 --- /dev/null +++ b/test/elf/Hexagon/zerofillquick-sdata.test @@ -0,0 +1,18 @@ +# This tests that a typeZeroFillFast atom is associated with a section that has +# the correct memory size. + +RUN: lld -flavor gnu -target hexagon %p/Inputs/sdata1.o %p/Inputs/sdata2.o \ +RUN: -o %t --noinhibit-exec -static +RUN: llvm-readobj -sections %t | FileCheck -check-prefix=CHECKSECTIONSANDSIZE %s + +CHECKSECTIONSANDSIZE: Section { +CHECKSECTIONSANDSIZE: Name: .sdata (13) +CHECKSECTIONSANDSIZE: Address: 0x1000 +CHECKSECTIONSANDSIZE: Offset: 0x1000 +CHECKSECTIONSANDSIZE: Size: 24 +CHECKSECTIONSANDSIZE: } +CHECKSECTIONSANDSIZE: Section { +CHECKSECTIONSANDSIZE: Name: .bss (20) +CHECKSECTIONSANDSIZE: Address: 0x1018 +CHECKSECTIONSANDSIZE: Offset: 0x1018 +CHECKSECTIONSANDSIZE: } diff --git a/test/elf/Inputs/abs-test.i386 b/test/elf/Inputs/abs-test.i386 Binary files differnew file mode 100644 index 0000000000000..8556c24da551a --- /dev/null +++ b/test/elf/Inputs/abs-test.i386 diff --git a/test/elf/Inputs/bar.o.x86-64 b/test/elf/Inputs/bar.o.x86-64 Binary files differnew file mode 100644 index 0000000000000..467485f0bb96a --- /dev/null +++ b/test/elf/Inputs/bar.o.x86-64 diff --git a/test/elf/Inputs/branch-test.hexagon b/test/elf/Inputs/branch-test.hexagon Binary files differnew file mode 100644 index 0000000000000..1ffb47228e326 --- /dev/null +++ b/test/elf/Inputs/branch-test.hexagon diff --git a/test/elf/Inputs/branch-test.ppc b/test/elf/Inputs/branch-test.ppc Binary files differnew file mode 100644 index 0000000000000..4698941e75039 --- /dev/null +++ b/test/elf/Inputs/branch-test.ppc diff --git a/test/elf/Inputs/consecutive-weak-defs.o.yaml b/test/elf/Inputs/consecutive-weak-defs.o.yaml new file mode 100644 index 0000000000000..144c2426bae1b --- /dev/null +++ b/test/elf/Inputs/consecutive-weak-defs.o.yaml @@ -0,0 +1,66 @@ +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 554889E5E8000000005DC3554889E5B8640000005DC3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x0000000000000005 + Symbol: my_weak_func + Type: R_X86_64_PC32 + Addend: -4 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + Global: + - Name: my_func + Type: STT_FUNC + Section: .text + Size: 0x000000000000000B + Weak: + - Name: my_weak_func + Type: STT_FUNC + Section: .text + Value: 0x000000000000000B + Size: 0x000000000000000B + - Name: my_weak_func2 + Type: STT_FUNC + Section: .text + Value: 0x000000000000000B + Size: 0x000000000000000B + - Name: my_weak_func3 + Type: STT_FUNC + Section: .text + Value: 0x000000000000000B + Size: 0x000000000000000B +... diff --git a/test/elf/Inputs/constants-merge.x86-64 b/test/elf/Inputs/constants-merge.x86-64 Binary files differnew file mode 100644 index 0000000000000..0087eb8f8cb59 --- /dev/null +++ b/test/elf/Inputs/constants-merge.x86-64 diff --git a/test/elf/Inputs/constdata.x86-64 b/test/elf/Inputs/constdata.x86-64 Binary files differnew file mode 100644 index 0000000000000..d877a55353570 --- /dev/null +++ b/test/elf/Inputs/constdata.x86-64 diff --git a/test/elf/Inputs/foo.o.x86-64 b/test/elf/Inputs/foo.o.x86-64 Binary files differnew file mode 100644 index 0000000000000..72a89eefa8edf --- /dev/null +++ b/test/elf/Inputs/foo.o.x86-64 diff --git a/test/elf/Inputs/globalconst.c b/test/elf/Inputs/globalconst.c new file mode 100644 index 0000000000000..08395a88e7ca3 --- /dev/null +++ b/test/elf/Inputs/globalconst.c @@ -0,0 +1,2 @@ +/* compile the code with -fmerge-all-constants */ +const char mystr[] = "foobar"; diff --git a/test/elf/Inputs/globalconst.o.x86-64 b/test/elf/Inputs/globalconst.o.x86-64 Binary files differnew file mode 100644 index 0000000000000..d8e266e763348 --- /dev/null +++ b/test/elf/Inputs/globalconst.o.x86-64 diff --git a/test/elf/Inputs/gotpcrel.S b/test/elf/Inputs/gotpcrel.S new file mode 100644 index 0000000000000..300675f596121 --- /dev/null +++ b/test/elf/Inputs/gotpcrel.S @@ -0,0 +1,11 @@ + .text + .globl main + .align 16, 0x90 + .type main,@function +main: # @main + movq blah@GOTPCREL(%rip), %rax + movq main@GOTPCREL(%rip), %rax + ret + + .weak blah + .type blah,@function diff --git a/test/elf/Inputs/gotpcrel.x86-64 b/test/elf/Inputs/gotpcrel.x86-64 Binary files differnew file mode 100644 index 0000000000000..f9c61d0edf15d --- /dev/null +++ b/test/elf/Inputs/gotpcrel.x86-64 diff --git a/test/elf/Inputs/group-cmd-search-1.ls b/test/elf/Inputs/group-cmd-search-1.ls new file mode 100644 index 0000000000000..965369309a20a --- /dev/null +++ b/test/elf/Inputs/group-cmd-search-1.ls @@ -0,0 +1 @@ +GROUP ( shared.so-x86-64 ) diff --git a/test/elf/Inputs/group-cmd-search-2.ls b/test/elf/Inputs/group-cmd-search-2.ls new file mode 100644 index 0000000000000..f1eee6a35efa1 --- /dev/null +++ b/test/elf/Inputs/group-cmd-search-2.ls @@ -0,0 +1 @@ +GROUP ( /shared.so-x86-64 ) diff --git a/test/elf/Inputs/group-cmd-search-3.ls b/test/elf/Inputs/group-cmd-search-3.ls new file mode 100644 index 0000000000000..83ce0ca62fb1e --- /dev/null +++ b/test/elf/Inputs/group-cmd-search-3.ls @@ -0,0 +1 @@ +GROUP ( -l:shared.so-x86-64 -lfnarchive ) diff --git a/test/elf/Inputs/ifunc.S b/test/elf/Inputs/ifunc.S new file mode 100644 index 0000000000000..0ac1f5a5b0bba --- /dev/null +++ b/test/elf/Inputs/ifunc.S @@ -0,0 +1,21 @@ + .text + .globl hey; + .type hey, @function; +hey: + .type hey, @gnu_indirect_function; + leaq __hey_1(%rip), %rax + ret + + .text + .type __hey_1, @function; + .globl __hey_1; +__hey_1: + movq $42, %rax + ret + + .text + .type plt, @function; + .globl plt; +plt: + call hey@PLT + ret diff --git a/test/elf/Inputs/ifunc.cpp b/test/elf/Inputs/ifunc.cpp new file mode 100644 index 0000000000000..2e520277d36c4 --- /dev/null +++ b/test/elf/Inputs/ifunc.cpp @@ -0,0 +1,3 @@ +extern "C" int hey(); + +int main() { return hey(); } diff --git a/test/elf/Inputs/ifunc.cpp.x86-64 b/test/elf/Inputs/ifunc.cpp.x86-64 Binary files differnew file mode 100644 index 0000000000000..20c812cabd2c7 --- /dev/null +++ b/test/elf/Inputs/ifunc.cpp.x86-64 diff --git a/test/elf/Inputs/ifunc.x86-64 b/test/elf/Inputs/ifunc.x86-64 Binary files differnew file mode 100644 index 0000000000000..36c8e04ca837f --- /dev/null +++ b/test/elf/Inputs/ifunc.x86-64 diff --git a/test/elf/Inputs/init_array.x86-64 b/test/elf/Inputs/init_array.x86-64 Binary files differnew file mode 100644 index 0000000000000..2425c227fd422 --- /dev/null +++ b/test/elf/Inputs/init_array.x86-64 diff --git a/test/elf/Inputs/libfnarchive.a b/test/elf/Inputs/libfnarchive.a Binary files differnew file mode 100644 index 0000000000000..753acd6e2c658 --- /dev/null +++ b/test/elf/Inputs/libfnarchive.a diff --git a/test/elf/Inputs/libifunc.x86-64.so b/test/elf/Inputs/libifunc.x86-64.so Binary files differnew file mode 100644 index 0000000000000..0f05b308ce78e --- /dev/null +++ b/test/elf/Inputs/libifunc.x86-64.so diff --git a/test/elf/Inputs/libundef.so b/test/elf/Inputs/libundef.so Binary files differnew file mode 100644 index 0000000000000..41f2a668f360b --- /dev/null +++ b/test/elf/Inputs/libundef.so diff --git a/test/elf/Inputs/libweaksym.so b/test/elf/Inputs/libweaksym.so Binary files differnew file mode 100755 index 0000000000000..7ff4ea56ae60c --- /dev/null +++ b/test/elf/Inputs/libweaksym.so diff --git a/test/elf/Inputs/main-with-global-def.o.yaml b/test/elf/Inputs/main-with-global-def.o.yaml new file mode 100644 index 0000000000000..55029614e1e89 --- /dev/null +++ b/test/elf/Inputs/main-with-global-def.o.yaml @@ -0,0 +1,56 @@ +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: 554889E5B8C80000005DC3554889E54883EC10C745FC00000000B000E8000000004883C4105DC3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x000000000000001D + Symbol: my_func + Type: R_X86_64_PC32 + Addend: -4 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + Global: + - Name: main + Type: STT_FUNC + Section: .text + Value: 0x000000000000000B + Size: 0x000000000000001C + - Name: my_weak_func2 + Type: STT_FUNC + Section: .text + Size: 0x000000000000000B + - Name: my_func +... diff --git a/test/elf/Inputs/mainobj.x86_64 b/test/elf/Inputs/mainobj.x86_64 Binary files differnew file mode 100644 index 0000000000000..d0f29418237de --- /dev/null +++ b/test/elf/Inputs/mainobj.x86_64 diff --git a/test/elf/Inputs/object-test.elf-hexagon b/test/elf/Inputs/object-test.elf-hexagon Binary files differnew file mode 100644 index 0000000000000..bfc85a7d0b762 --- /dev/null +++ b/test/elf/Inputs/object-test.elf-hexagon diff --git a/test/elf/Inputs/object-test.elf-i386 b/test/elf/Inputs/object-test.elf-i386 Binary files differnew file mode 100644 index 0000000000000..872684c7fcc1a --- /dev/null +++ b/test/elf/Inputs/object-test.elf-i386 diff --git a/test/elf/Inputs/phdr.i386 b/test/elf/Inputs/phdr.i386 Binary files differnew file mode 100644 index 0000000000000..7c83dd3148912 --- /dev/null +++ b/test/elf/Inputs/phdr.i386 diff --git a/test/elf/Inputs/quickdata-sort-test.o.elf-hexagon b/test/elf/Inputs/quickdata-sort-test.o.elf-hexagon Binary files differnew file mode 100644 index 0000000000000..03d0287071295 --- /dev/null +++ b/test/elf/Inputs/quickdata-sort-test.o.elf-hexagon diff --git a/test/elf/Inputs/quickdata-sortcommon-test.o.elf-hexagon b/test/elf/Inputs/quickdata-sortcommon-test.o.elf-hexagon Binary files differnew file mode 100644 index 0000000000000..410a3d47d3201 --- /dev/null +++ b/test/elf/Inputs/quickdata-sortcommon-test.o.elf-hexagon diff --git a/test/elf/Inputs/quickdata-test.elf-hexagon b/test/elf/Inputs/quickdata-test.elf-hexagon Binary files differnew file mode 100644 index 0000000000000..c3ae53a6babe6 --- /dev/null +++ b/test/elf/Inputs/quickdata-test.elf-hexagon diff --git a/test/elf/Inputs/reloc-test.elf-i386 b/test/elf/Inputs/reloc-test.elf-i386 Binary files differnew file mode 100644 index 0000000000000..1a5558131fffa --- /dev/null +++ b/test/elf/Inputs/reloc-test.elf-i386 diff --git a/test/elf/Inputs/reloc-xb.x86 b/test/elf/Inputs/reloc-xb.x86 Binary files differnew file mode 100644 index 0000000000000..4d9770aa23564 --- /dev/null +++ b/test/elf/Inputs/reloc-xb.x86 diff --git a/test/elf/Inputs/reloc-xt.x86 b/test/elf/Inputs/reloc-xt.x86 Binary files differnew file mode 100644 index 0000000000000..dcdfbfb09f710 --- /dev/null +++ b/test/elf/Inputs/reloc-xt.x86 diff --git a/test/elf/Inputs/relocs-dynamic.x86-64 b/test/elf/Inputs/relocs-dynamic.x86-64 Binary files differnew file mode 100644 index 0000000000000..0c44924d18e69 --- /dev/null +++ b/test/elf/Inputs/relocs-dynamic.x86-64 diff --git a/test/elf/Inputs/relocs.x86-64 b/test/elf/Inputs/relocs.x86-64 Binary files differnew file mode 100644 index 0000000000000..112dfa733db9e --- /dev/null +++ b/test/elf/Inputs/relocs.x86-64 diff --git a/test/elf/Inputs/responsefile b/test/elf/Inputs/responsefile new file mode 100644 index 0000000000000..2fe657a0e3b0e --- /dev/null +++ b/test/elf/Inputs/responsefile @@ -0,0 +1 @@ +--inresponsefile diff --git a/test/elf/Inputs/rodata-test.hexagon b/test/elf/Inputs/rodata-test.hexagon Binary files differnew file mode 100644 index 0000000000000..f448748e2abe2 --- /dev/null +++ b/test/elf/Inputs/rodata-test.hexagon diff --git a/test/elf/Inputs/rodata-test.i386 b/test/elf/Inputs/rodata-test.i386 Binary files differnew file mode 100644 index 0000000000000..09f71eb599695 --- /dev/null +++ b/test/elf/Inputs/rodata-test.i386 diff --git a/test/elf/Inputs/rodata.c b/test/elf/Inputs/rodata.c new file mode 100644 index 0000000000000..b43c9c9458986 --- /dev/null +++ b/test/elf/Inputs/rodata.c @@ -0,0 +1,4 @@ +const unsigned char *str = "llvm"; +int foo() { + return str[0]; +} diff --git a/test/elf/Inputs/rodata.o b/test/elf/Inputs/rodata.o Binary files differnew file mode 100644 index 0000000000000..f13ddc9bba98b --- /dev/null +++ b/test/elf/Inputs/rodata.o diff --git a/test/elf/Inputs/section-test.i386 b/test/elf/Inputs/section-test.i386 Binary files differnew file mode 100644 index 0000000000000..2b447a9f8f5b3 --- /dev/null +++ b/test/elf/Inputs/section-test.i386 diff --git a/test/elf/Inputs/shared.c b/test/elf/Inputs/shared.c new file mode 100644 index 0000000000000..2be91c4b9e454 --- /dev/null +++ b/test/elf/Inputs/shared.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +extern int i; +int i = 42; + +// Undefined weak function in a dynamic library. +__attribute__((weak)) void weakfoo(); + +// Regular function in a dynamic library. +void foo() { + // Try to call weakfoo so that the reference to weekfoo will be included in + // the resulting .so file. + if (weakfoo) + weakfoo(); + puts("Fooo!!"); +} diff --git a/test/elf/Inputs/shared.so-x86-64 b/test/elf/Inputs/shared.so-x86-64 Binary files differnew file mode 100644 index 0000000000000..0240f0e674315 --- /dev/null +++ b/test/elf/Inputs/shared.so-x86-64 diff --git a/test/elf/Inputs/stripped-empty.x86_64 b/test/elf/Inputs/stripped-empty.x86_64 Binary files differnew file mode 100644 index 0000000000000..7368ba280d795 --- /dev/null +++ b/test/elf/Inputs/stripped-empty.x86_64 diff --git a/test/elf/Inputs/target-test.hexagon b/test/elf/Inputs/target-test.hexagon Binary files differnew file mode 100644 index 0000000000000..7da114561920c --- /dev/null +++ b/test/elf/Inputs/target-test.hexagon diff --git a/test/elf/Inputs/target-test.ppc b/test/elf/Inputs/target-test.ppc Binary files differnew file mode 100644 index 0000000000000..001be338c20db --- /dev/null +++ b/test/elf/Inputs/target-test.ppc diff --git a/test/elf/Inputs/tls.S b/test/elf/Inputs/tls.S new file mode 100644 index 0000000000000..7d9eab647b533 --- /dev/null +++ b/test/elf/Inputs/tls.S @@ -0,0 +1,50 @@ + .text + .globl main + .align 16, 0x90 + .type main,@function +main: # @main + callq GOTTPOFF + addl %fs:tls1@TPOFF, %eax + addl %fs:tls0@TPOFF, %eax + addl %fs:tls2@TPOFF, %eax + ret + + .text + .globl GOTTPOFF + .type GOTTPOFF,@function +GOTTPOFF: + movq tls2@GOTTPOFF(%rip), %rax + movl %fs:0(%rax), %eax + ret + + .text + .globl TLSLD + .type TLSLD,@function +TLSLD: + leaq tls0@tlsld(%rip), %rdi + call __tls_get_addr@plt + leaq tls0@dtpoff(%rax), %rax + ret + + .type tls0,@object # @tls0 + .section .tbss,"awT",@nobits + .globl tls0 + .align 4 +tls0: + .long 0 # 0x0 + .size tls0, 4 + + .type tls1,@object # @tls1 + .globl tls1 + .align 4 +tls1: + .long 0 # 0x0 + .size tls1, 4 + + .type tls2,@object # @tls2 + .section .tdata,"awT",@progbits + .globl tls2 + .align 4 +tls2: + .long 1 # 0x1 + .size tls2, 4 diff --git a/test/elf/Inputs/tls.c b/test/elf/Inputs/tls.c new file mode 100644 index 0000000000000..6723507565626 --- /dev/null +++ b/test/elf/Inputs/tls.c @@ -0,0 +1,11 @@ +extern __thread int tls0; +extern __thread int tls1; +extern __thread int tls2; + +__thread int tls0 = 0; +__thread int tls1 = 0; +__thread int tls2 = 1; + +int main() { + return tls0 + tls1 + tls2; +} diff --git a/test/elf/Inputs/tls.x86-64 b/test/elf/Inputs/tls.x86-64 Binary files differnew file mode 100644 index 0000000000000..b420ce89b19b9 --- /dev/null +++ b/test/elf/Inputs/tls.x86-64 diff --git a/test/elf/Inputs/tlsAddr.x86-64 b/test/elf/Inputs/tlsAddr.x86-64 Binary files differnew file mode 100644 index 0000000000000..16cc9ab25abf0 --- /dev/null +++ b/test/elf/Inputs/tlsAddr.x86-64 diff --git a/test/elf/Inputs/tlsaddr.c b/test/elf/Inputs/tlsaddr.c new file mode 100644 index 0000000000000..f62d57b3bfb49 --- /dev/null +++ b/test/elf/Inputs/tlsaddr.c @@ -0,0 +1,8 @@ +__thread int tls0 = 0; +__thread int tls1 = 0; +__thread int tls2 = 1; +__thread int tls3 = 2; + +int main() { + return tls0 + tls1 + tls2; +} diff --git a/test/elf/Inputs/undef-from-main-so.c b/test/elf/Inputs/undef-from-main-so.c new file mode 100644 index 0000000000000..f1cb63db7adac --- /dev/null +++ b/test/elf/Inputs/undef-from-main-so.c @@ -0,0 +1 @@ +int x[2] = {1, 2}; diff --git a/test/elf/Inputs/undef-from-main.c b/test/elf/Inputs/undef-from-main.c new file mode 100644 index 0000000000000..366d934dd84fa --- /dev/null +++ b/test/elf/Inputs/undef-from-main.c @@ -0,0 +1,5 @@ +extern int x[2]; + +int main() { + x[0] = 2; +} diff --git a/test/elf/Inputs/undef-pc32.o b/test/elf/Inputs/undef-pc32.o Binary files differnew file mode 100644 index 0000000000000..954916d59991a --- /dev/null +++ b/test/elf/Inputs/undef-pc32.o diff --git a/test/elf/Inputs/undef.o b/test/elf/Inputs/undef.o Binary files differnew file mode 100644 index 0000000000000..3c9b60c6ca336 --- /dev/null +++ b/test/elf/Inputs/undef.o diff --git a/test/elf/Inputs/undef2-so.o.yaml b/test/elf/Inputs/undef2-so.o.yaml new file mode 100644 index 0000000000000..9ecf374ced11c --- /dev/null +++ b/test/elf/Inputs/undef2-so.o.yaml @@ -0,0 +1,50 @@ +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000001 + Content: 554889E5488B05000000008B005DC3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x0000000000000007 + Symbol: myexportedsymbol + Type: R_X86_64_GOTPCREL + Addend: -4 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + Global: + - Name: func + Type: STT_FUNC + Section: .text + Size: 0x000000000000000F + - Name: _GLOBAL_OFFSET_TABLE_ + - Name: myexportedsymbol diff --git a/test/elf/Inputs/use-shared-32s.c b/test/elf/Inputs/use-shared-32s.c new file mode 100644 index 0000000000000..63054cb036ea3 --- /dev/null +++ b/test/elf/Inputs/use-shared-32s.c @@ -0,0 +1,8 @@ +void foo(); + +void (*func)(); + +int main() { + func = foo; + func(); +} diff --git a/test/elf/Inputs/use-shared-32s.x86-64 b/test/elf/Inputs/use-shared-32s.x86-64 Binary files differnew file mode 100644 index 0000000000000..7e223677b93fc --- /dev/null +++ b/test/elf/Inputs/use-shared-32s.x86-64 diff --git a/test/elf/Inputs/use-shared.c b/test/elf/Inputs/use-shared.c new file mode 100644 index 0000000000000..b370eaa4fac2d --- /dev/null +++ b/test/elf/Inputs/use-shared.c @@ -0,0 +1,7 @@ +extern int i; +void foo(); + +int main() { + foo(); + return i; +} diff --git a/test/elf/Inputs/use-shared.x86-64 b/test/elf/Inputs/use-shared.x86-64 Binary files differnew file mode 100644 index 0000000000000..25e1eb87a2b41 --- /dev/null +++ b/test/elf/Inputs/use-shared.x86-64 diff --git a/test/elf/Inputs/weaksym.o b/test/elf/Inputs/weaksym.o Binary files differnew file mode 100644 index 0000000000000..010ce704a3135 --- /dev/null +++ b/test/elf/Inputs/weaksym.o diff --git a/test/elf/Inputs/writersyms.o b/test/elf/Inputs/writersyms.o Binary files differnew file mode 100644 index 0000000000000..7cd472d99d77d --- /dev/null +++ b/test/elf/Inputs/writersyms.o diff --git a/test/elf/Inputs/x86-64-relocs.S b/test/elf/Inputs/x86-64-relocs.S new file mode 100644 index 0000000000000..2547c0f19bb0e --- /dev/null +++ b/test/elf/Inputs/x86-64-relocs.S @@ -0,0 +1,12 @@ + .text + .globl main + .align 16, 0x90 + .type main,@function +main: # @main + call foo@PLT + ret + + .globl foo + .type foo,@function +foo: + ret diff --git a/test/elf/Mips/base-address-64.test b/test/elf/Mips/base-address-64.test new file mode 100644 index 0000000000000..07110e7f918f8 --- /dev/null +++ b/test/elf/Mips/base-address-64.test @@ -0,0 +1,78 @@ +# Check executable base address configuration. Base address should be +# equal to 0x400000 and the MIPS_BASE_ADDRESS dynamic tag's value should +# be the same. +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mips64el --noinhibit-exec -o %t.exe %t.o +# RUN: llvm-readobj -dynamic-table -program-headers %t.exe | FileCheck %s + +# CHECK: DynamicSection [ (13 entries) +# CHECK: Tag Type Name/Value +# CHECK-NEXT: 0x0000000000000004 HASH 0x{{[0-9A-F]+}} +# CHECK-NEXT: 0x0000000000000005 STRTAB 0x{{[0-9A-F]+}} +# CHECK-NEXT: 0x0000000000000006 SYMTAB 0x{{[0-9A-F]+}} +# CHECK-NEXT: 0x000000000000000A STRSZ 1 (bytes) +# CHECK-NEXT: 0x000000000000000B SYMENT 24 (bytes) +# CHECK-NEXT: 0x0000000070000001 MIPS_RLD_VERSION 1 +# CHECK-NEXT: 0x0000000070000005 MIPS_FLAGS NOTPOT +# CHECK-NEXT: 0x0000000070000006 MIPS_BASE_ADDRESS 0x120000000 +# CHECK-NEXT: 0x000000007000000A MIPS_LOCAL_GOTNO 2 +# CHECK-NEXT: 0x0000000070000011 MIPS_SYMTABNO 1 +# CHECK-NEXT: 0x0000000070000013 MIPS_GOTSYM 0x1 +# CHECK-NEXT: 0x0000000000000003 PLTGOT 0x120001000 +# CHECK-NEXT: 0x0000000000000000 NULL 0x0 +# CHECK-NEXT: ] + +# CHECK: ProgramHeaders [ +# CHECK: ProgramHeader { +# CHECK: Type: PT_PHDR (0x6) +# CHECK: Offset: 0x40 +# CHECK: VirtualAddress: 0x{{[0-9A-F]+}} +# CHECK: } +# CHECK: ProgramHeader { +# CHECK: Type: PT_INTERP (0x3) +# CHECK: Offset: 0x190 +# CHECK: VirtualAddress: 0x{{[0-9A-F]+}} +# CHECK: } +# CHECK: ProgramHeader { +# CHECK: Type: PT_LOAD (0x1) +# CHECK-NEXT: Offset: 0x0 +# CHECK-NEXT: VirtualAddress: 0x120000000 + +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ARCH_64R2 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x08 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x10 + Size: 0x00 + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x10 + Size: 0x00 + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + Global: + - Name: main + Section: .text diff --git a/test/elf/Mips/base-address.test b/test/elf/Mips/base-address.test new file mode 100644 index 0000000000000..f55091f84c33d --- /dev/null +++ b/test/elf/Mips/base-address.test @@ -0,0 +1,109 @@ +# Check executable base address configuration. Base address should be +# equal to 0x400000 and the MIPS_BASE_ADDRESS dynamic tag's value should +# be the same. +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mipsel --noinhibit-exec -o %t.exe %t.o +# RUN: llvm-readobj -dynamic-table -program-headers %t.exe | FileCheck %s + +# CHECK: DynamicSection [ (13 entries) +# CHECK: Tag Type Name/Value +# CHECK-NEXT: 0x00000004 HASH 0x{{[0-9A-F]+}} +# CHECK-NEXT: 0x00000005 STRTAB 0x{{[0-9A-F]+}} +# CHECK-NEXT: 0x00000006 SYMTAB 0x{{[0-9A-F]+}} +# CHECK-NEXT: 0x0000000A STRSZ 1 (bytes) +# CHECK-NEXT: 0x0000000B SYMENT 16 (bytes) +# CHECK-NEXT: 0x70000001 MIPS_RLD_VERSION 1 +# CHECK-NEXT: 0x70000005 MIPS_FLAGS NOTPOT +# CHECK-NEXT: 0x70000006 MIPS_BASE_ADDRESS 0x400000 +# CHECK-NEXT: 0x7000000A MIPS_LOCAL_GOTNO 2 +# CHECK-NEXT: 0x70000011 MIPS_SYMTABNO 1 +# CHECK-NEXT: 0x70000013 MIPS_GOTSYM 0x1 +# CHECK-NEXT: 0x00000003 PLTGOT 0x401000 +# CHECK-NEXT: 0x00000000 NULL 0x0 +# CHECK-NEXT: ] + +# CHECK: ProgramHeaders [ +# CHECK: ProgramHeader { +# CHECK: Type: PT_PHDR (0x6) +# CHECK: Offset: 0x34 +# CHECK: VirtualAddress: 0x{{[0-9A-F]+}} +# CHECK: } +# CHECK: ProgramHeader { +# CHECK: Type: PT_INTERP (0x3) +# CHECK: Offset: 0xF4 +# CHECK: VirtualAddress: 0x{{[0-9A-F]+}} +# CHECK: } +# CHECK: ProgramHeader { +# CHECK: Type: PT_LOAD (0x1) +# CHECK: Offset: 0x0 +# CHECK: VirtualAddress: 0x{{[0-9A-F]+}} +# CHECK: } +# CHECK: ProgramHeader { +# CHECK: Type: PT_LOAD (0x1) +# CHECK: Offset: 0x1000 +# CHECK: VirtualAddress: 0x{{[0-9A-F]+}} +# CHECK: } +# CHECK: ProgramHeader { +# CHECK: Type: PT_LOAD (0x1) +# CHECK: Offset: 0x2000 +# CHECK: VirtualAddress: 0x{{[0-9A-F]+}} +# CHECK: } +# CHECK: ProgramHeader { +# CHECK: Type: PT_DYNAMIC (0x2) +# CHECK: Offset: 0x12C +# CHECK: VirtualAddress: 0x{{[0-9A-F]+}} +# CHECK: } +# CHECK: ] + +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_ABI_O32, EF_MIPS_ARCH_32 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x04 + Size: 0x00 + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x04 + Size: 0x00 + - Name: .reginfo + Type: SHT_MIPS_REGINFO + Flags: [ SHF_ALLOC ] + AddressAlign: 0x01 + Size: 0x18 + - Name: .MIPS.abiflags + Type: SHT_MIPS_ABIFLAGS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x08 + Content: '000020010101000100000000000000000000000000000000' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .reginfo + Type: STT_SECTION + Section: .reginfo + - Name: .MIPS.abiflags + Type: STT_SECTION + Section: .MIPS.abiflags + Global: + - Name: main + Section: .text diff --git a/test/elf/Mips/ctors-order.test b/test/elf/Mips/ctors-order.test new file mode 100644 index 0000000000000..344dcd5fc516c --- /dev/null +++ b/test/elf/Mips/ctors-order.test @@ -0,0 +1,163 @@ +# Check ordering of .ctors.* sections. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-crtbeginS.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-crtendS.o +# RUN: yaml2obj -format=elf -docnum 3 %s > %t-obj.o +# RUN: lld -flavor gnu -target mipsel -shared --output-filetype=yaml \ +# RUN: %t-crtbeginS.o %t-obj.o %t-crtendS.o | FileCheck %s +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so \ +# RUN: %t-crtbeginS.o %t-obj.o %t-crtendS.o +# RUN: llvm-objdump -s %t.so | FileCheck -check-prefix=RAW %s + +# CHECK: defined-atoms: +# CHECK-NEXT: - type: data +# CHECK-NEXT: alignment: 2^2 +# CHECK-NEXT: section-choice: custom-required +# CHECK-NEXT: section-name: .ctors +# CHECK-NEXT: references: +# CHECK-NEXT: - kind: layout-after +# CHECK-NEXT: offset: 0 +# CHECK-NEXT: target: __CTOR_LIST__ +# CHECK-NEXT: - name: __CTOR_LIST__ +# CHECK-NEXT: type: data +# CHECK-NEXT: content: [ FF, FF, FF, FF ] +# CHECK-NEXT: alignment: 2^2 +# CHECK-NEXT: section-choice: custom-required +# CHECK-NEXT: section-name: .ctors +# CHECK-NEXT: - type: data +# CHECK-NEXT: content: [ 11, 11, 11, 11 ] +# CHECK-NEXT: alignment: 2^2 +# CHECK-NEXT: section-choice: custom-required +# CHECK-NEXT: section-name: .ctors.1 +# CHECK-NEXT: - type: data +# CHECK-NEXT: content: [ 22, 22, 22, 22 ] +# CHECK-NEXT: alignment: 2^2 +# CHECK-NEXT: section-choice: custom-required +# CHECK-NEXT: section-name: .ctors.2 +# CHECK-NEXT: - ref-name: L003 +# CHECK-NEXT: type: data +# CHECK-NEXT: alignment: 2^2 +# CHECK-NEXT: section-choice: custom-required +# CHECK-NEXT: section-name: .ctors +# CHECK-NEXT: references: +# CHECK-NEXT: - kind: layout-after +# CHECK-NEXT: offset: 0 +# CHECK-NEXT: target: __CTOR_END__ +# CHECK-NEXT: - name: __CTOR_END__ +# CHECK-NEXT: type: data +# CHECK-NEXT: content: [ 00, 00, 00, 00 ] +# CHECK-NEXT: alignment: 2^2 +# CHECK-NEXT: section-choice: custom-required +# CHECK-NEXT: section-name: .ctors + +# RAW: Contents of section .ctors: +# RAW-NEXT: 1000 ffffffff 11111111 22222222 00000000 +# crtbeginS.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ] +Sections: + - Name: .ctors + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x04 + Content: 'FFFFFFFF' + +Symbols: + Local: + - Name: .ctors + Type: STT_SECTION + Section: .ctors + - Name: __CTOR_LIST__ + Type: STT_OBJECT + Section: .ctors + +# crtendS.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x0F + - Name: .rel.text + Type: SHT_REL + Link: .symtab + Info: .text + AddressAlign: 0x04 + Relocations: + - Offset: 0x00 + Symbol: .ctors + Type: R_MIPS_HI16 + - Offset: 0x04 + Symbol: .ctors + Type: R_MIPS_LO16 + - Offset: 0x08 + Symbol: .ctors + Type: R_MIPS_HI16 + - Offset: 0x0C + Symbol: .ctors + Type: R_MIPS_LO16 + - Name: .ctors + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .ctors + Type: STT_SECTION + Section: .ctors + - Name: __CTOR_END__ + Type: STT_OBJECT + Section: .ctors + - Name: __do_global_ctors_aux + Type: STT_FUNC + Section: .text + +# obj.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32 ] +Sections: + - Name: .ctors.2 + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x04 + Content: '22222222' + - Name: .ctors.1 + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x04 + Content: '11111111' + +Symbols: + Local: + - Name: .ctors.2 + Type: STT_SECTION + Section: .ctors.2 + - Name: .ctors.1 + Type: STT_SECTION + Section: .ctors.1 +... diff --git a/test/elf/Mips/dt-textrel-64.test b/test/elf/Mips/dt-textrel-64.test new file mode 100644 index 0000000000000..32cc99e54b2fd --- /dev/null +++ b/test/elf/Mips/dt-textrel-64.test @@ -0,0 +1,74 @@ +# Check that if a dynamic relocation R_MIPS_64 modify a read-only section, +# .dynamic section contains the DT_TEXTREL tag. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mips64el -shared -o %t.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mips64el -e T0 -o %t.exe %t-o.o %t.so +# RUN: llvm-readobj -dynamic-table %t.exe | FileCheck %s + +# CHECK: 0x{{[0-9A-F]+}} TEXTREL + +# so.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ARCH_64] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x4 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 0x08 + +# o.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ARCH_64] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x4 + Size: 0x8 + + - Name: .rel.text + Type: SHT_RELA + Link: .symtab + Info: .text + AddressAlign: 0x04 + Relocations: + - Offset: 0 + Symbol: T1 + Type: R_MIPS_64 + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + + Global: + - Name: T0 + Type: STT_FUNC + Section: .text + Size: 0x8 + - Name: T1 +... diff --git a/test/elf/Mips/dt-textrel.test b/test/elf/Mips/dt-textrel.test new file mode 100644 index 0000000000000..ca854dff8e583 --- /dev/null +++ b/test/elf/Mips/dt-textrel.test @@ -0,0 +1,74 @@ +# Check that if a dynamic relocation modify a read-only section, +# .dynamic section contains the DT_TEXTREL tag. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o.o %t.so +# RUN: llvm-readobj -dynamic-table %t.exe | FileCheck %s + +# CHECK: 0x{{[0-9A-F]+}} TEXTREL + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x04 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 0x04 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Content: '00000000' + + - Name: .rel.text + Type: SHT_REL + Link: .symtab + Info: .text + AddressAlign: 0x04 + Relocations: + - Offset: 0 + Symbol: T1 + Type: R_MIPS_32 + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + + Global: + - Name: T0 + Type: STT_FUNC + Section: .text + Size: 0x04 + - Name: T1 +... diff --git a/test/elf/Mips/dynlib-dynamic.test b/test/elf/Mips/dynlib-dynamic.test new file mode 100644 index 0000000000000..54afdec263a63 --- /dev/null +++ b/test/elf/Mips/dynlib-dynamic.test @@ -0,0 +1,110 @@ +# Check MIPS specific tags in the dynamic table. + +# Build shared library +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mipsel -shared --noinhibit-exec -o %t.so %t.o +# RUN: llvm-readobj -dynamic-table %t.so | FileCheck %s + +# CHECK: Format: ELF32-mips +# CHECK: Arch: mipsel +# CHECK: AddressSize: 32bit +# CHECK: LoadName: +# CHECK: DynamicSection [ (13 entries) +# CHECK: Tag Type Name/Value +# CHECK-NEXT: 0x00000004 HASH 0x{{[0-9A-F]+}} +# CHECK-NEXT: 0x00000005 STRTAB 0x{{[0-9A-F]+}} +# CHECK-NEXT: 0x00000006 SYMTAB 0x{{[0-9A-F]+}} +# CHECK-NEXT: 0x0000000A STRSZ 17 (bytes) +# CHECK-NEXT: 0x0000000B SYMENT 16 (bytes) +# CHECK-NEXT: 0x70000001 MIPS_RLD_VERSION 1 +# CHECK-NEXT: 0x70000005 MIPS_FLAGS NOTPOT +# CHECK-NEXT: 0x70000006 MIPS_BASE_ADDRESS 0x0 +# CHECK-NEXT: 0x7000000A MIPS_LOCAL_GOTNO 4 +# CHECK-NEXT: 0x70000011 MIPS_SYMTABNO 4 +# CHECK-NEXT: 0x70000013 MIPS_GOTSYM 0x2 +# CHECK-NEXT: 0x00000003 PLTGOT 0x1000 +# CHECK-NEXT: 0x00000000 NULL 0x0 +# CHECK-NEXT: ] + +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x18 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: '$.str1' + Type: R_MIPS_GOT16 + - Offset: 0x04 + Symbol: '$.str1' + Type: R_MIPS_LO16 + - Offset: 0x08 + Symbol: '$.str2' + Type: R_MIPS_GOT16 + - Offset: 0x0C + Symbol: '$.str2' + Type: R_MIPS_LO16 + - Offset: 0x10 + Symbol: glob2 + Type: R_MIPS_CALL16 + - Offset: 0x14 + Symbol: ext1 + Type: R_MIPS_CALL16 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x04 + Size: 0x00 + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x04 + Size: 0x00 + - Name: .rodata.str1 + Type: SHT_PROGBITS + AddressAlign: 0x01 + Size: 0x05 + - Name: .rodata.str2 + Type: SHT_PROGBITS + AddressAlign: 0x01 + Size: 0x05 + +Symbols: + Local: + - Name: '$.str1' + Section: .rodata.str1 + - Name: '$.str2' + Section: .rodata.str2 + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .rodata.str1 + Type: STT_SECTION + Section: .rodata.str1 + - Name: .rodata.str2 + Type: STT_SECTION + Section: .rodata.str2 + Global: + - Name: glob + Section: .text + - Name: ext1 + - Name: glob2 diff --git a/test/elf/Mips/dynlib-dynsym-micro.test b/test/elf/Mips/dynlib-dynsym-micro.test new file mode 100644 index 0000000000000..4d75945af7b04 --- /dev/null +++ b/test/elf/Mips/dynlib-dynsym-micro.test @@ -0,0 +1,208 @@ +# 1. Check sorting of .dynsym content accordingly to .got section +# in case of using microMIPS relocations. +# 2. Check that microMIPS records in a dynamic symbol table have: +# - cleared the STO_MIPS_MICROMIPS flag +# - adjusted adress + +# Build shared library +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mipsel -shared --noinhibit-exec -o %t-so %t.o +# RUN: llvm-readobj -dyn-symbols %t-so | FileCheck -check-prefix=CHECK-DYN %s + +# Build shared library (yaml format) +# RUN: lld -flavor gnu -target mipsel -shared --noinhibit-exec \ +# RUN: --output-filetype=yaml -o %t-yaml %t.o +# RUN: FileCheck -check-prefix=CHECK-GOT %s < %t-yaml + +# CHECK-DYN: Format: ELF32-mips +# CHECK-DYN: Arch: mipsel +# CHECK-DYN: AddressSize: 32bit +# CHECK-DYN: LoadName: +# CHECK-DYN: DynamicSymbols [ +# CHECK-DYN: Symbol { +# CHECK-DYN: Name: @ (0) +# CHECK-DYN: Value: 0x0 +# CHECK-DYN: Size: 0 +# CHECK-DYN: Binding: Local (0x0) +# CHECK-DYN: Type: None (0x0) +# CHECK-DYN: Other: 0 +# CHECK-DYN: Section: Undefined (0x0) +# CHECK-DYN: } +# CHECK-DYN: Symbol { +# CHECK-DYN: Name: bar@ (5) +# CHECK-DYN: Value: 0x139 +# CHECK-DYN: Size: 4 +# CHECK-DYN: Binding: Global (0x1) +# CHECK-DYN: Type: Function (0x2) +# CHECK-DYN: Other: 0 +# CHECK-DYN: Section: .text (0x4) +# CHECK-DYN: } +# CHECK-DYN: Symbol { +# CHECK-DYN: Name: foo@ (1) +# CHECK-DYN: Value: 0x121 +# CHECK-DYN: Size: 24 +# CHECK-DYN: Binding: Global (0x1) +# CHECK-DYN: Type: Function (0x2) +# CHECK-DYN: Other: 0 +# CHECK-DYN: Section: .text (0x4) +# CHECK-DYN: } +# CHECK-DYN: Symbol { +# CHECK-DYN: Name: ext1@ (9) +# CHECK-DYN: Value: 0x0 +# CHECK-DYN: Size: 0 +# CHECK-DYN: Binding: Global (0x1) +# CHECK-DYN: Type: None (0x0) +# CHECK-DYN: Other: 0 +# CHECK-DYN: Section: Undefined (0x0) +# CHECK-DYN: } +# CHECK-DYN: Symbol { +# CHECK-DYN: Name: ext2@ (14) +# CHECK-DYN: Value: 0x0 +# CHECK-DYN: Size: 0 +# CHECK-DYN: Binding: Global (0x1) +# CHECK-DYN: Type: None (0x0) +# CHECK-DYN: Other: 0 +# CHECK-DYN: Section: Undefined (0x0) +# CHECK-DYN: } +# CHECK-DYN: ] + +# CHECK-GOT: - type: got +# CHECK-GOT: content: [ 00, 00, 00, 00 ] +# CHECK-GOT: alignment: 2^2 +# CHECK-GOT: section-choice: custom-required +# CHECK-GOT: section-name: .got +# CHECK-GOT: permissions: rw- +# CHECK-GOT: - type: got +# CHECK-GOT: content: [ 00, 00, 00, 80 ] +# CHECK-GOT: alignment: 2^2 +# CHECK-GOT: section-choice: custom-required +# CHECK-GOT: section-name: .got +# CHECK-GOT: permissions: rw- +# CHECK-GOT: - ref-name: L000 +# CHECK-GOT: type: got +# CHECK-GOT: content: [ 00, 00, 00, 00 ] +# CHECK-GOT: alignment: 2^2 +# CHECK-GOT: section-choice: custom-required +# CHECK-GOT: section-name: .got +# CHECK-GOT: permissions: rw- +# CHECK-GOT: references: +# CHECK-GOT: - kind: LLD_R_MIPS_32_HI16 +# CHECK-GOT: offset: 0 +# CHECK-GOT: target: L007 +# CHECK-GOT: - ref-name: L002 +# CHECK-GOT: type: got +# CHECK-GOT: content: [ 00, 00, 00, 00 ] +# CHECK-GOT: alignment: 2^2 +# CHECK-GOT: section-choice: custom-required +# CHECK-GOT: section-name: .got +# CHECK-GOT: permissions: rw- +# CHECK-GOT: references: +# CHECK-GOT: - kind: LLD_R_MIPS_32_HI16 +# CHECK-GOT: offset: 0 +# CHECK-GOT: target: L008 +# CHECK-GOT: - ref-name: L004 +# CHECK-GOT: type: got +# CHECK-GOT: content: [ 00, 00, 00, 00 ] +# CHECK-GOT: alignment: 2^2 +# CHECK-GOT: section-choice: custom-required +# CHECK-GOT: section-name: .got +# CHECK-GOT: permissions: rw- +# CHECK-GOT: references: +# CHECK-GOT: - kind: LLD_R_MIPS_GLOBAL_GOT +# CHECK-GOT: offset: 0 +# CHECK-GOT: target: foo +# CHECK-GOT: - ref-name: L005 +# CHECK-GOT: type: got +# CHECK-GOT: content: [ 00, 00, 00, 00 ] +# CHECK-GOT: alignment: 2^2 +# CHECK-GOT: section-choice: custom-required +# CHECK-GOT: section-name: .got +# CHECK-GOT: permissions: rw- +# CHECK-GOT: references: +# CHECK-GOT: - kind: LLD_R_MIPS_GLOBAL_GOT +# CHECK-GOT: offset: 0 +# CHECK-GOT: target: ext1 +# CHECK-GOT: - ref-name: L006 +# CHECK-GOT: type: got +# CHECK-GOT: content: [ 00, 00, 00, 00 ] +# CHECK-GOT: alignment: 2^2 +# CHECK-GOT: section-choice: custom-required +# CHECK-GOT: section-name: .got +# CHECK-GOT: permissions: rw- +# CHECK-GOT: references: +# CHECK-GOT: - kind: LLD_R_MIPS_GLOBAL_GOT +# CHECK-GOT: offset: 0 +# CHECK-GOT: target: ext2 + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, + EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x1C + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: .rodata.str1 + Type: R_MICROMIPS_GOT16 + - Offset: 0x04 + Symbol: .rodata.str1 + Type: R_MICROMIPS_LO16 + - Offset: 0x08 + Symbol: .rodata.str2 + Type: R_MICROMIPS_GOT16 + - Offset: 0x0C + Symbol: .rodata.str2 + Type: R_MICROMIPS_LO16 + - Offset: 0x10 + Symbol: foo + Type: R_MICROMIPS_CALL16 + - Offset: 0x14 + Symbol: ext1 + Type: R_MICROMIPS_CALL16 + - Offset: 0x18 + Symbol: ext2 + Type: R_MICROMIPS_CALL16 + - Name: .rodata.str1 + Type: SHT_PROGBITS + AddressAlign: 0x01 + Size: 0x05 + - Name: .rodata.str2 + Type: SHT_PROGBITS + AddressAlign: 0x01 + Size: 0x05 + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .rodata.str1 + Type: STT_SECTION + Section: .rodata.str1 + - Name: .rodata.str2 + Type: STT_SECTION + Section: .rodata.str2 + Global: + - Name: bar + Section: .text + Value: 0x18 + Other: [ STO_MIPS_MICROMIPS ] + - Name: foo + Section: .text + Other: [ STO_MIPS_MICROMIPS ] + - Name: ext1 + - Name: ext2 +... diff --git a/test/elf/Mips/dynlib-dynsym.test b/test/elf/Mips/dynlib-dynsym.test new file mode 100644 index 0000000000000..d480c3cbbe417 --- /dev/null +++ b/test/elf/Mips/dynlib-dynsym.test @@ -0,0 +1,202 @@ +# Check sorting of .dynsym content accordingly to .got section. + +# Build shared library +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mipsel -shared --noinhibit-exec -o %t-so %t.o +# RUN: llvm-readobj -dyn-symbols %t-so | FileCheck -check-prefix=CHECK-DYN %s + +# Build shared library (yaml format) +# RUN: lld -flavor gnu -target mipsel -shared --noinhibit-exec \ +# RUN: --output-filetype=yaml -o %t-yaml %t.o +# RUN: FileCheck -check-prefix=CHECK-GOT %s < %t-yaml + +# CHECK-DYN: Format: ELF32-mips +# CHECK-DYN: Arch: mipsel +# CHECK-DYN: AddressSize: 32bit +# CHECK-DYN: LoadName: +# CHECK-DYN: DynamicSymbols [ +# CHECK-DYN: Symbol { +# CHECK-DYN: Name: @ (0) +# CHECK-DYN: Value: 0x0 +# CHECK-DYN: Size: 0 +# CHECK-DYN: Binding: Local (0x0) +# CHECK-DYN: Type: None (0x0) +# CHECK-DYN: Other: 0 +# CHECK-DYN: Section: Undefined (0x0) +# CHECK-DYN: } +# CHECK-DYN: Symbol { +# CHECK-DYN: Name: bar@ (5) +# CHECK-DYN: Value: 0x138 +# CHECK-DYN: Size: 4 +# CHECK-DYN: Binding: Global (0x1) +# CHECK-DYN: Type: Function (0x2) +# CHECK-DYN: Other: 0 +# CHECK-DYN: Section: .text (0x4) +# CHECK-DYN: } +# CHECK-DYN: Symbol { +# CHECK-DYN: Name: foo@ (1) +# CHECK-DYN: Value: 0x120 +# CHECK-DYN: Size: 24 +# CHECK-DYN: Binding: Global (0x1) +# CHECK-DYN: Type: Function (0x2) +# CHECK-DYN: Other: 0 +# CHECK-DYN: Section: .text (0x4) +# CHECK-DYN: } +# CHECK-DYN: Symbol { +# CHECK-DYN: Name: ext1@ (9) +# CHECK-DYN: Value: 0x0 +# CHECK-DYN: Size: 0 +# CHECK-DYN: Binding: Global (0x1) +# CHECK-DYN: Type: None (0x0) +# CHECK-DYN: Other: 0 +# CHECK-DYN: Section: Undefined (0x0) +# CHECK-DYN: } +# CHECK-DYN: Symbol { +# CHECK-DYN: Name: ext2@ (14) +# CHECK-DYN: Value: 0x0 +# CHECK-DYN: Size: 0 +# CHECK-DYN: Binding: Global (0x1) +# CHECK-DYN: Type: None (0x0) +# CHECK-DYN: Other: 0 +# CHECK-DYN: Section: Undefined (0x0) +# CHECK-DYN: } +# CHECK-DYN: ] + +# CHECK-GOT: - type: got +# CHECK-GOT: content: [ 00, 00, 00, 00 ] +# CHECK-GOT: alignment: 2^2 +# CHECK-GOT: section-choice: custom-required +# CHECK-GOT: section-name: .got +# CHECK-GOT: permissions: rw- +# CHECK-GOT: - type: got +# CHECK-GOT: content: [ 00, 00, 00, 80 ] +# CHECK-GOT: alignment: 2^2 +# CHECK-GOT: section-choice: custom-required +# CHECK-GOT: section-name: .got +# CHECK-GOT: permissions: rw- +# CHECK-GOT: - ref-name: L000 +# CHECK-GOT: type: got +# CHECK-GOT: content: [ 00, 00, 00, 00 ] +# CHECK-GOT: alignment: 2^2 +# CHECK-GOT: section-choice: custom-required +# CHECK-GOT: section-name: .got +# CHECK-GOT: permissions: rw- +# CHECK-GOT: references: +# CHECK-GOT: - kind: LLD_R_MIPS_32_HI16 +# CHECK-GOT: offset: 0 +# CHECK-GOT: target: L007 +# CHECK-GOT: - ref-name: L002 +# CHECK-GOT: type: got +# CHECK-GOT: content: [ 00, 00, 00, 00 ] +# CHECK-GOT: alignment: 2^2 +# CHECK-GOT: section-choice: custom-required +# CHECK-GOT: section-name: .got +# CHECK-GOT: permissions: rw- +# CHECK-GOT: references: +# CHECK-GOT: - kind: LLD_R_MIPS_32_HI16 +# CHECK-GOT: offset: 0 +# CHECK-GOT: target: L008 +# CHECK-GOT: - ref-name: L004 +# CHECK-GOT: type: got +# CHECK-GOT: content: [ 00, 00, 00, 00 ] +# CHECK-GOT: alignment: 2^2 +# CHECK-GOT: section-choice: custom-required +# CHECK-GOT: section-name: .got +# CHECK-GOT: permissions: rw- +# CHECK-GOT: references: +# CHECK-GOT: - kind: LLD_R_MIPS_GLOBAL_GOT +# CHECK-GOT: offset: 0 +# CHECK-GOT: target: foo +# CHECK-GOT: - ref-name: L005 +# CHECK-GOT: type: got +# CHECK-GOT: content: [ 00, 00, 00, 00 ] +# CHECK-GOT: alignment: 2^2 +# CHECK-GOT: section-choice: custom-required +# CHECK-GOT: section-name: .got +# CHECK-GOT: permissions: rw- +# CHECK-GOT: references: +# CHECK-GOT: - kind: LLD_R_MIPS_GLOBAL_GOT +# CHECK-GOT: offset: 0 +# CHECK-GOT: target: ext1 +# CHECK-GOT: - ref-name: L006 +# CHECK-GOT: type: got +# CHECK-GOT: content: [ 00, 00, 00, 00 ] +# CHECK-GOT: alignment: 2^2 +# CHECK-GOT: section-choice: custom-required +# CHECK-GOT: section-name: .got +# CHECK-GOT: permissions: rw- +# CHECK-GOT: references: +# CHECK-GOT: - kind: LLD_R_MIPS_GLOBAL_GOT +# CHECK-GOT: offset: 0 +# CHECK-GOT: target: ext2 + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, + EF_MIPS_ARCH_32 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x1C + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: .rodata.str1 + Type: R_MIPS_GOT16 + - Offset: 0x04 + Symbol: .rodata.str1 + Type: R_MIPS_LO16 + - Offset: 0x08 + Symbol: .rodata.str2 + Type: R_MIPS_GOT16 + - Offset: 0x0C + Symbol: .rodata.str2 + Type: R_MIPS_LO16 + - Offset: 0x10 + Symbol: foo + Type: R_MIPS_CALL16 + - Offset: 0x14 + Symbol: ext1 + Type: R_MIPS_CALL16 + - Offset: 0x18 + Symbol: ext2 + Type: R_MIPS_CALL16 + - Name: .rodata.str1 + Type: SHT_PROGBITS + AddressAlign: 0x01 + Size: 0x05 + - Name: .rodata.str2 + Type: SHT_PROGBITS + AddressAlign: 0x01 + Size: 0x05 + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .rodata.str1 + Type: STT_SECTION + Section: .rodata.str1 + - Name: .rodata.str2 + Type: STT_SECTION + Section: .rodata.str2 + Global: + - Name: bar + Section: .text + Value: 0x18 + - Name: foo + Section: .text + - Name: ext1 + - Name: ext2 +... diff --git a/test/elf/Mips/dynlib-fileheader-64.test b/test/elf/Mips/dynlib-fileheader-64.test new file mode 100644 index 0000000000000..206f4fa7794d7 --- /dev/null +++ b/test/elf/Mips/dynlib-fileheader-64.test @@ -0,0 +1,72 @@ +# Check ELF Header for 64-bit shared library. + +# Build shared library +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mips64el -shared -o %t.so %t.o +# RUN: llvm-readobj -file-headers %t.so | FileCheck %s + +# CHECK: Format: ELF64-mips +# CHECK: Arch: mips64el +# CHECK: AddressSize: 64bit +# CHECK: LoadName: +# CHECK: ElfHeader { +# CHECK: Ident { +# CHECK: Magic: (7F 45 4C 46) +# CHECK: Class: 64-bit (0x2) +# CHECK: DataEncoding: LittleEndian (0x1) +# CHECK: FileVersion: 1 +# CHECK: OS/ABI: SystemV (0x0) +# CHECK: ABIVersion: 0 +# CHECK: Unused: (00 00 00 00 00 00 00) +# CHECK: } +# CHECK: Type: SharedObject (0x3) +# CHECK: Machine: EM_MIPS (0x8) +# CHECK: Version: 1 +# CHECK: Entry: 0x170 +# CHECK: ProgramHeaderOffset: 0x40 +# CHECK: SectionHeaderOffset: 0x2140 +# CHECK: Flags [ (0x80000006) +# CHECK: EF_MIPS_ARCH_64R2 (0x80000000) +# CHECK: EF_MIPS_CPIC (0x4) +# CHECK: EF_MIPS_PIC (0x2) +# CHECK: ] +# CHECK: HeaderSize: 64 +# CHECK: ProgramHeaderEntrySize: 56 +# CHECK: ProgramHeaderCount: 4 +# CHECK: SectionHeaderEntrySize: 64 +# CHECK: SectionHeaderCount: 11 +# CHECK: StringTableSectionIndex: 8 +# CHECK: } + +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ARCH_64R2 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x10 + Size: 0x08 + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + Global: + - Name: data + Type: STT_OBJECT + Section: .data + Size: 0x04 +... diff --git a/test/elf/Mips/dynlib-fileheader-micro-64.test b/test/elf/Mips/dynlib-fileheader-micro-64.test new file mode 100644 index 0000000000000..c03a951671eac --- /dev/null +++ b/test/elf/Mips/dynlib-fileheader-micro-64.test @@ -0,0 +1,75 @@ +# Check ELF Header for shared library in case of microMIPS symbols. + +# Build shared library +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mips64el -shared -o %t.so %t.o +# RUN: llvm-readobj -file-headers %t.so | FileCheck %s + +# CHECK: Format: ELF64-mips +# CHECK-NEXT: Arch: mips64el +# CHECK-NEXT: AddressSize: 64bit +# CHECK-NEXT: LoadName: +# CHECK-NEXT: ElfHeader { +# CHECK-NEXT: Ident { +# CHECK-NEXT: Magic: (7F 45 4C 46) +# CHECK-NEXT: Class: 64-bit (0x2) +# CHECK-NEXT: DataEncoding: LittleEndian (0x1) +# CHECK-NEXT: FileVersion: 1 +# CHECK-NEXT: OS/ABI: SystemV (0x0) +# CHECK-NEXT: ABIVersion: 0 +# CHECK-NEXT: Unused: (00 00 00 00 00 00 00) +# CHECK-NEXT: } +# CHECK-NEXT: Type: SharedObject (0x3) +# CHECK-NEXT: Machine: EM_MIPS (0x8) +# CHECK-NEXT: Version: 1 +# CHECK-NEXT: Entry: 0x170 +# CHECK-NEXT: ProgramHeaderOffset: 0x40 +# CHECK-NEXT: SectionHeaderOffset: 0x2140 +# CHECK-NEXT: Flags [ (0x82000007) +# CHECK-NEXT: EF_MIPS_ARCH_64R2 (0x80000000) +# CHECK-NEXT: EF_MIPS_CPIC (0x4) +# CHECK-NEXT: EF_MIPS_MICROMIPS (0x2000000) +# CHECK-NEXT: EF_MIPS_NOREORDER (0x1) +# CHECK-NEXT: EF_MIPS_PIC (0x2) +# CHECK-NEXT: ] +# CHECK-NEXT: HeaderSize: 64 +# CHECK-NEXT: ProgramHeaderEntrySize: 56 +# CHECK-NEXT: ProgramHeaderCount: 4 +# CHECK-NEXT: SectionHeaderEntrySize: 64 +# CHECK-NEXT: SectionHeaderCount: 11 +# CHECK-NEXT: StringTableSectionIndex: 8 +# CHECK-NEXT:} + +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_MICROMIPS, EF_MIPS_ARCH_64R2 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x08 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x10 + Size: 0x08 + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + Global: + - Name: foo + Type: STT_FUNC + Section: .text + Size: 0x08 + Other: [ STO_MIPS_MICROMIPS ] diff --git a/test/elf/Mips/dynlib-fileheader-micro.test b/test/elf/Mips/dynlib-fileheader-micro.test new file mode 100644 index 0000000000000..139b3aa626c90 --- /dev/null +++ b/test/elf/Mips/dynlib-fileheader-micro.test @@ -0,0 +1,82 @@ +# Check ELF Header for shared library in case of microMIPS symbols. + +# Build shared library +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t.o +# RUN: llvm-readobj -file-headers %t.so | FileCheck %s + +# CHECK: Format: ELF32-mips +# CHECK-NEXT: Arch: mipsel +# CHECK-NEXT: AddressSize: 32bit +# CHECK-NEXT: LoadName: +# CHECK-NEXT: ElfHeader { +# CHECK-NEXT: Ident { +# CHECK-NEXT: Magic: (7F 45 4C 46) +# CHECK-NEXT: Class: 32-bit (0x1) +# CHECK-NEXT: DataEncoding: LittleEndian (0x1) +# CHECK-NEXT: FileVersion: 1 +# CHECK-NEXT: OS/ABI: SystemV (0x0) +# CHECK-NEXT: ABIVersion: 0 +# CHECK-NEXT: Unused: (00 00 00 00 00 00 00) +# CHECK-NEXT: } +# CHECK-NEXT: Type: SharedObject (0x3) +# CHECK-NEXT: Machine: EM_MIPS (0x8) +# CHECK-NEXT: Version: 1 +# CHECK-NEXT: Entry: 0x100 +# CHECK-NEXT: ProgramHeaderOffset: 0x34 +# CHECK-NEXT: SectionHeaderOffset: 0x2100 +# CHECK-NEXT: Flags [ (0x72001007) +# CHECK-NEXT: EF_MIPS_ABI_O32 (0x1000) +# CHECK-NEXT: EF_MIPS_ARCH_32R2 (0x70000000) +# CHECK-NEXT: EF_MIPS_CPIC (0x4) +# CHECK-NEXT: EF_MIPS_MICROMIPS (0x2000000) +# CHECK-NEXT: EF_MIPS_NOREORDER (0x1) +# CHECK-NEXT: EF_MIPS_PIC (0x2) +# CHECK-NEXT: ] +# CHECK-NEXT: HeaderSize: 52 +# CHECK-NEXT: ProgramHeaderEntrySize: 32 +# CHECK-NEXT: ProgramHeaderCount: 4 +# CHECK-NEXT: SectionHeaderEntrySize: 40 +# CHECK-NEXT: SectionHeaderCount: 11 +# CHECK-NEXT: StringTableSectionIndex: 8 +# CHECK-NEXT:} + +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x04 + Size: 0x00 + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x04 + Size: 0x00 + - Name: .reginfo + Type: SHT_MIPS_REGINFO + Flags: [ SHF_ALLOC ] + AddressAlign: 0x01 + Size: 0x18 + - Name: .MIPS.abiflags + Type: SHT_MIPS_ABIFLAGS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x08 + Size: 0x18 + +Symbols: + Global: + - Name: glob + Section: .text + Other: [ STO_MIPS_MICROMIPS ] diff --git a/test/elf/Mips/dynlib-fileheader.test b/test/elf/Mips/dynlib-fileheader.test new file mode 100644 index 0000000000000..5dd9d6a64a713 --- /dev/null +++ b/test/elf/Mips/dynlib-fileheader.test @@ -0,0 +1,80 @@ +# Check ELF Header for shared library. + +# Build shared library +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t.o +# RUN: llvm-readobj -file-headers %t.so | FileCheck %s + +# CHECK: Format: ELF32-mips +# CHECK: Arch: mipsel +# CHECK: AddressSize: 32bit +# CHECK: LoadName: +# CHECK: ElfHeader { +# CHECK: Ident { +# CHECK: Magic: (7F 45 4C 46) +# CHECK: Class: 32-bit (0x1) +# CHECK: DataEncoding: LittleEndian (0x1) +# CHECK: FileVersion: 1 +# CHECK: OS/ABI: SystemV (0x0) +# CHECK: ABIVersion: 0 +# CHECK: Unused: (00 00 00 00 00 00 00) +# CHECK: } +# CHECK: Type: SharedObject (0x3) +# CHECK: Machine: EM_MIPS (0x8) +# CHECK: Version: 1 +# CHECK: Entry: 0x100 +# CHECK: ProgramHeaderOffset: 0x34 +# CHECK: SectionHeaderOffset: 0x2100 +# CHECK: Flags [ (0x70001007) +# CHECK: EF_MIPS_ABI_O32 (0x1000) +# CHECK: EF_MIPS_ARCH_32R2 (0x70000000) +# CHECK: EF_MIPS_CPIC (0x4) +# CHECK: EF_MIPS_NOREORDER (0x1) +# CHECK: EF_MIPS_PIC (0x2) +# CHECK: ] +# CHECK: HeaderSize: 52 +# CHECK: ProgramHeaderEntrySize: 32 +# CHECK: ProgramHeaderCount: 4 +# CHECK: SectionHeaderEntrySize: 40 +# CHECK: SectionHeaderCount: 11 +# CHECK: StringTableSectionIndex: 8 +# CHECK:} + +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x04 + Size: 0x00 + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x04 + Size: 0x00 + - Name: .reginfo + Type: SHT_MIPS_REGINFO + Flags: [ SHF_ALLOC ] + AddressAlign: 0x01 + Size: 0x18 + - Name: .MIPS.abiflags + Type: SHT_MIPS_ABIFLAGS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x08 + Size: 0x18 + +Symbols: + Global: + - Name: glob + Section: .text diff --git a/test/elf/Mips/dynsym-table-1.test b/test/elf/Mips/dynsym-table-1.test new file mode 100644 index 0000000000000..43c48e7304058 --- /dev/null +++ b/test/elf/Mips/dynsym-table-1.test @@ -0,0 +1,127 @@ +# Check that LLD does not populate an executable file dynamic symbol table +# by unnecessary symbols. +# 1. bar.so defines T2 +# 2. foo.so defines T1 and references T2 +# 3. main.o reference T1 +# 4. a.out dynamic table should contain T1 entry only + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-bar.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-foo.o +# RUN: yaml2obj -format=elf -docnum 3 %s > %t-main.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t-bar.so %t-bar.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t-foo.so %t-foo.o %t-bar.so +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe --as-needed \ +# RUN: %t-main.o %t-foo.so %t-bar.so +# RUN: llvm-readobj -dt -dynamic-table %t.exe | FileCheck %s + +# CHECK: DynamicSymbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: @ (0) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local (0x0) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: T1@ ({{.*}}) +# CHECK-NEXT: Value: {{.*}} +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 8 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# CHECK: 0x00000003 PLTGOT 0x401000 +# CHECK-NEXT: 0x00000001 NEEDED SharedLibrary (dynsym-table-1.test.tmp-foo.so) +# CHECK-NEXT: 0x00000000 NULL 0x0 + +# bar.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x08 +Symbols: + Global: + - Name: T2 + Type: STT_FUNC + Section: .text + Size: 0x08 + +# foo.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x08 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x08 + Info: .text + Relocations: + - Offset: 0 + Symbol: T2 + Type: R_MIPS_CALL16 +Symbols: + Global: + - Name: T1 + Type: STT_FUNC + Section: .text + Size: 0x08 + - Name: T2 + +# main.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x08 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x08 + Info: .text + Relocations: + - Offset: 0 + Symbol: T1 + Type: R_MIPS_32 +Symbols: + Global: + - Name: T0 + Type: STT_FUNC + Section: .text + Size: 0x08 + - Name: T1 +... diff --git a/test/elf/Mips/dynsym-table-2.test b/test/elf/Mips/dynsym-table-2.test new file mode 100644 index 0000000000000..538aa758910e0 --- /dev/null +++ b/test/elf/Mips/dynsym-table-2.test @@ -0,0 +1,105 @@ +# Check that LLD does not populate a shared library dynamic symbol table +# by unnecessary symbols. +# 1. bar.so defines T2 and T3 +# 2. foo.so defines T1 and references T2 +# 4. foo.so dynamic table should contain T1 and T2 entries only + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-bar.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-foo.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t-bar.so %t-bar.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t-foo.so %t-foo.o %t-bar.so +# RUN: llvm-readobj -dt -dynamic-table %t-foo.so | FileCheck %s + +# CHECK: DynamicSymbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: @ (0) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local (0x0) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: T1@ ({{.*}}) +# CHECK-NEXT: Value: {{.*}} +# CHECK-NEXT: Size: 8 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text (0x4) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: T2@ ({{.*}}) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# CHECK: 0x00000003 PLTGOT 0x1000 +# CHECK-NEXT: 0x00000001 NEEDED SharedLibrary (dynsym-table-2.test.tmp-bar.so) +# CHECK-NEXT: 0x00000000 NULL 0x0 + +# bar.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x08 +Symbols: + Global: + - Name: T2 + Type: STT_FUNC + Section: .text + Size: 0x04 + - Name: T3 + Type: STT_FUNC + Section: .text + Value: 0x04 + Size: 0x04 + +# foo.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x08 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x08 + Info: .text + Relocations: + - Offset: 0 + Symbol: T2 + Type: R_MIPS_CALL16 +Symbols: + Global: + - Name: T1 + Type: STT_FUNC + Section: .text + Size: 0x08 + - Name: T2 +... diff --git a/test/elf/Mips/e-flags-merge-1-64.test b/test/elf/Mips/e-flags-merge-1-64.test new file mode 100644 index 0000000000000..d5719539baaa5 --- /dev/null +++ b/test/elf/Mips/e-flags-merge-1-64.test @@ -0,0 +1,30 @@ +# Check that the linker shows an error when object +# file has unsupported ASE flags. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-mips16.o +# RUN: not lld -flavor gnu -target mips64el -e T -o %t.exe %t-mips16.o 2>&1 | \ +# RUN: FileCheck -check-prefix=MIPS16 %s + +# MIPS16: Unsupported extension: MIPS16 + +# mips16.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ARCH_64, EF_MIPS_ARCH_ASE_M16] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x08 + +Symbols: + Global: + - Name: T + Section: .text +... diff --git a/test/elf/Mips/e-flags-merge-1.test b/test/elf/Mips/e-flags-merge-1.test new file mode 100644 index 0000000000000..1f1d7aca6c41d --- /dev/null +++ b/test/elf/Mips/e-flags-merge-1.test @@ -0,0 +1,56 @@ +# Check that the linker shows an error when object file has missed +# or unsupported ABI and ARCH flags or unsupported ASE flags. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-no-abi.o +# RUN: not lld -flavor gnu -target mipsel -e T -o %t.exe %t-no-abi.o 2>&1 | \ +# RUN: FileCheck -check-prefix=INVALID-ABI %s + +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-mips16.o +# RUN: not lld -flavor gnu -target mipsel -e T -o %t.exe %t-mips16.o 2>&1 | \ +# RUN: FileCheck -check-prefix=MIPS16 %s + +# INVALID-ABI: Unsupported ABI +# MIPS16: Unsupported extension: MIPS16 + +# no-abi.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Global: + - Name: T + Section: .text + +# mips16.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_32, EF_MIPS_ARCH_ASE_M16] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Global: + - Name: T + Section: .text +... diff --git a/test/elf/Mips/e-flags-merge-10.test b/test/elf/Mips/e-flags-merge-10.test new file mode 100644 index 0000000000000..a0aa45d5f2c89 --- /dev/null +++ b/test/elf/Mips/e-flags-merge-10.test @@ -0,0 +1,43 @@ +# Check that LLD shows an error and does not link files with mips32r2 +# and mips32r6 instructions sets. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-32r2.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-32r6.o + +# RUN: not lld -flavor gnu -target mipsel -shared -o %t.so \ +# RUN: %t-32r2.o %t-32r6.o 2>&1 | FileCheck %s + +# CHECK: Linking modules with incompatible ISA + +# 32r2.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + +# 32r6.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R6] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 +... diff --git a/test/elf/Mips/e-flags-merge-11.test b/test/elf/Mips/e-flags-merge-11.test new file mode 100644 index 0000000000000..b4c0039bd1989 --- /dev/null +++ b/test/elf/Mips/e-flags-merge-11.test @@ -0,0 +1,43 @@ +# Check that LLD shows an error and does not link files with mips64r2 +# and mips64r6 instructions sets. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-64r2.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-64r6.o + +# RUN: not lld -flavor gnu -target mips64el -shared -o %t.so \ +# RUN: %t-64r2.o %t-64r6.o 2>&1 | FileCheck %s + +# CHECK: Linking modules with incompatible ISA + +# 64r2.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ARCH_64R2] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + +# 64r6.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ARCH_64R6] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 +... diff --git a/test/elf/Mips/e-flags-merge-2-64.test b/test/elf/Mips/e-flags-merge-2-64.test new file mode 100644 index 0000000000000..a169e7ea1645b --- /dev/null +++ b/test/elf/Mips/e-flags-merge-2-64.test @@ -0,0 +1,33 @@ +# Check that the linker copies ELF header flags from the single input object +# file to the generated executable + +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mips64el -e T -o %t.exe %t.o +# RUN: llvm-readobj -file-headers %t.exe | FileCheck %s + +# CHECK: Flags [ (0x62000001) +# CHECK-NEXT: EF_MIPS_ARCH_64 (0x60000000) +# CHECK-NEXT: EF_MIPS_MICROMIPS (0x2000000) +# CHECK-NEXT: EF_MIPS_NOREORDER (0x1) +# CHECK-NEXT: ] + +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_ARCH_64, EF_MIPS_NOREORDER, EF_MIPS_MICROMIPS ] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x08 + +Symbols: + Global: + - Name: T + Section: .text +... diff --git a/test/elf/Mips/e-flags-merge-2.test b/test/elf/Mips/e-flags-merge-2.test new file mode 100644 index 0000000000000..41d4a0b0c45e1 --- /dev/null +++ b/test/elf/Mips/e-flags-merge-2.test @@ -0,0 +1,35 @@ +# Check that the linker copies ELF header flags from the single input object +# file to the generated executable + +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mipsel -e T -o %t.exe %t.o +# RUN: llvm-readobj -file-headers %t.exe | FileCheck %s + +# CHECK: Flags [ (0x52001001) +# CHECK-NEXT: EF_MIPS_ABI_O32 (0x1000) +# CHECK-NEXT: EF_MIPS_ARCH_32 (0x50000000) +# CHECK-NEXT: EF_MIPS_MICROMIPS (0x2000000) +# CHECK-NEXT: EF_MIPS_NOREORDER (0x1) +# CHECK-NEXT: ] + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_32, + EF_MIPS_NOREORDER, EF_MIPS_MICROMIPS] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Global: + - Name: T + Section: .text +... diff --git a/test/elf/Mips/e-flags-merge-3-64.test b/test/elf/Mips/e-flags-merge-3-64.test new file mode 100644 index 0000000000000..54065a63fb949 --- /dev/null +++ b/test/elf/Mips/e-flags-merge-3-64.test @@ -0,0 +1,130 @@ +# Check PIC/CPIC flags merging in case of multiple input objects. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-none.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-cpic.o +# RUN: yaml2obj -format=elf -docnum 3 %s > %t-pic.o +# RUN: yaml2obj -format=elf -docnum 4 %s > %t-both.o + +# RUN: lld -flavor gnu -target mips64el -e T1 -o %t-abi1.exe \ +# RUN: %t-none.o %t-pic.o 2>&1 | FileCheck -check-prefix=ABI-CALLS-WARN %s +# RUN: llvm-readobj -file-headers %t-abi1.exe \ +# RUN: | FileCheck -check-prefix=ABI-CALLS1 %s + +# RUN: lld -flavor gnu -target mips64el -e T1 -o %t-abi2.exe \ +# RUN: %t-cpic.o %t-none.o 2>&1 | FileCheck -check-prefix=ABI-CALLS-WARN %s +# RUN: llvm-readobj -file-headers %t-abi2.exe \ +# RUN: | FileCheck -check-prefix=ABI-CALLS2 %s + +# RUN: lld -flavor gnu -target mips64el -e T2 -o %t-cpic.exe %t-cpic.o %t-pic.o +# RUN: llvm-readobj -file-headers %t-cpic.exe | FileCheck -check-prefix=CPIC %s + +# RUN: lld -flavor gnu -target mips64el -e T3 -o %t-both.exe %t-pic.o %t-both.o +# RUN: llvm-readobj -file-headers %t-both.exe | FileCheck -check-prefix=BOTH %s + +# ABI-CALLS-WARN: lld warning: linking abicalls and non-abicalls files + +# ABI-CALLS1: Flags [ (0x60000004) +# ABI-CALLS1-NEXT: EF_MIPS_ARCH_64 (0x60000000) +# ABI-CALLS1-NEXT: EF_MIPS_CPIC (0x4) +# ABI-CALLS1-NEXT: ] + +# ABI-CALLS2: Flags [ (0x60000004) +# ABI-CALLS2-NEXT: EF_MIPS_ARCH_64 (0x60000000) +# ABI-CALLS2-NEXT: EF_MIPS_CPIC (0x4) +# ABI-CALLS2-NEXT: ] + +# CPIC: Flags [ (0x60000004) +# CPIC-NEXT: EF_MIPS_ARCH_64 (0x60000000) +# CPIC-NEXT: EF_MIPS_CPIC (0x4) +# CPIC-NEXT: ] + +# BOTH: Flags [ (0x60000006) +# BOTH-NEXT: EF_MIPS_ARCH_64 (0x60000000) +# BOTH-NEXT: EF_MIPS_CPIC (0x4) +# BOTH-NEXT: EF_MIPS_PIC (0x2) +# BOTH-NEXT: ] + +# none.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ARCH_64] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x08 + +Symbols: + Global: + - Name: T1 + Section: .text + +# cpic.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ARCH_64, EF_MIPS_CPIC] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x08 + +Symbols: + Global: + - Name: T2 + Section: .text + +# pic.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ARCH_64, EF_MIPS_PIC] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x08 + +Symbols: + Global: + - Name: T3 + Section: .text + +# both.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ARCH_64, EF_MIPS_CPIC, EF_MIPS_PIC] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x08 + +Symbols: + Global: + - Name: T4 + Section: .text +... diff --git a/test/elf/Mips/e-flags-merge-3.test b/test/elf/Mips/e-flags-merge-3.test new file mode 100644 index 0000000000000..e2d9f6c2e2fc6 --- /dev/null +++ b/test/elf/Mips/e-flags-merge-3.test @@ -0,0 +1,134 @@ +# Check PIC/CPIC flags merging in case of multiple input objects. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-none.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-cpic.o +# RUN: yaml2obj -format=elf -docnum 3 %s > %t-pic.o +# RUN: yaml2obj -format=elf -docnum 4 %s > %t-both.o + +# RUN: lld -flavor gnu -target mipsel -e T1 -o %t-abi1.exe \ +# RUN: %t-none.o %t-pic.o 2>&1 | FileCheck -check-prefix=ABI-CALLS-WARN %s +# RUN: llvm-readobj -file-headers %t-abi1.exe \ +# RUN: | FileCheck -check-prefix=ABI-CALLS1 %s + +# RUN: lld -flavor gnu -target mipsel -e T1 -o %t-abi2.exe \ +# RUN: %t-cpic.o %t-none.o 2>&1 | FileCheck -check-prefix=ABI-CALLS-WARN %s +# RUN: llvm-readobj -file-headers %t-abi2.exe \ +# RUN: | FileCheck -check-prefix=ABI-CALLS2 %s + +# RUN: lld -flavor gnu -target mipsel -e T2 -o %t-cpic.exe %t-cpic.o %t-pic.o +# RUN: llvm-readobj -file-headers %t-cpic.exe | FileCheck -check-prefix=CPIC %s + +# RUN: lld -flavor gnu -target mipsel -e T3 -o %t-both.exe %t-pic.o %t-both.o +# RUN: llvm-readobj -file-headers %t-both.exe | FileCheck -check-prefix=BOTH %s + +# ABI-CALLS-WARN: lld warning: linking abicalls and non-abicalls files + +# ABI-CALLS1: Flags [ (0x50001004) +# ABI-CALLS1-NEXT: EF_MIPS_ABI_O32 (0x1000) +# ABI-CALLS1-NEXT: EF_MIPS_ARCH_32 (0x50000000) +# ABI-CALLS1-NEXT: EF_MIPS_CPIC (0x4) +# ABI-CALLS1-NEXT: ] + +# ABI-CALLS2: Flags [ (0x50001004) +# ABI-CALLS2-NEXT: EF_MIPS_ABI_O32 (0x1000) +# ABI-CALLS2-NEXT: EF_MIPS_ARCH_32 (0x50000000) +# ABI-CALLS2-NEXT: EF_MIPS_CPIC (0x4) +# ABI-CALLS2-NEXT: ] + +# CPIC: Flags [ (0x50001004) +# CPIC-NEXT: EF_MIPS_ABI_O32 (0x1000) +# CPIC-NEXT: EF_MIPS_ARCH_32 (0x50000000) +# CPIC-NEXT: EF_MIPS_CPIC (0x4) +# CPIC-NEXT: ] + +# BOTH: Flags [ (0x50001006) +# BOTH-NEXT: EF_MIPS_ABI_O32 (0x1000) +# BOTH-NEXT: EF_MIPS_ARCH_32 (0x50000000) +# BOTH-NEXT: EF_MIPS_CPIC (0x4) +# BOTH-NEXT: EF_MIPS_PIC (0x2) +# BOTH-NEXT: ] + +# none.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Global: + - Name: T1 + Section: .text + +# cpic.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_32, EF_MIPS_CPIC] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Global: + - Name: T2 + Section: .text + +# pic.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_32, EF_MIPS_PIC] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Global: + - Name: T3 + Section: .text + +# both.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_32, EF_MIPS_CPIC, EF_MIPS_PIC] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Global: + - Name: T4 + Section: .text +... diff --git a/test/elf/Mips/e-flags-merge-4-64.test b/test/elf/Mips/e-flags-merge-4-64.test new file mode 100644 index 0000000000000..9ffa613437111 --- /dev/null +++ b/test/elf/Mips/e-flags-merge-4-64.test @@ -0,0 +1,64 @@ +# Check ELF flags merging. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-none.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-noreorder.o +# RUN: yaml2obj -format=elf -docnum 3 %s > %t-micro.o + +# RUN: lld -flavor gnu -target mips64el -shared -o %t.so \ +# RUN: %t-none.o %t-noreorder.o %t-micro.o +# RUN: llvm-readobj -file-headers %t.so | FileCheck %s + +# CHECK: Flags [ (0x82000001) +# CHECK-NEXT: EF_MIPS_ARCH_64R2 (0x80000000) +# CHECK-NEXT: EF_MIPS_MICROMIPS (0x2000000) +# CHECK-NEXT: EF_MIPS_NOREORDER (0x1) +# CHECK-NEXT: ] + +# none.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ARCH_5] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x08 + +# noreorder.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ARCH_64, EF_MIPS_NOREORDER] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x08 + +# micro.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ARCH_64R2, EF_MIPS_MICROMIPS] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x08 +... diff --git a/test/elf/Mips/e-flags-merge-4.test b/test/elf/Mips/e-flags-merge-4.test new file mode 100644 index 0000000000000..096b04d676e9e --- /dev/null +++ b/test/elf/Mips/e-flags-merge-4.test @@ -0,0 +1,65 @@ +# Check ELF flags merging. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-none.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-noreorder.o +# RUN: yaml2obj -format=elf -docnum 3 %s > %t-micro.o + +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so \ +# RUN: %t-none.o %t-noreorder.o %t-micro.o +# RUN: llvm-readobj -file-headers %t.so | FileCheck %s + +# CHECK: Flags [ (0x52001001) +# CHECK-NEXT: EF_MIPS_ABI_O32 (0x1000) +# CHECK-NEXT: EF_MIPS_ARCH_32 (0x50000000) +# CHECK-NEXT: EF_MIPS_MICROMIPS (0x2000000) +# CHECK-NEXT: EF_MIPS_NOREORDER (0x1) +# CHECK-NEXT: ] + +# none.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + +# noreorder.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_32, EF_MIPS_NOREORDER] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + +# micro.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_32, EF_MIPS_MICROMIPS] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 +... diff --git a/test/elf/Mips/e-flags-merge-5-64.test b/test/elf/Mips/e-flags-merge-5-64.test new file mode 100644 index 0000000000000..e629aedbc154f --- /dev/null +++ b/test/elf/Mips/e-flags-merge-5-64.test @@ -0,0 +1,42 @@ +# Check that LLD does not allow to mix 32 and 64-bit MIPS object files. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-32.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-64.o + +# RUN: not lld -flavor gnu -target mips64el -shared -o %t.so \ +# RUN: %t-32.o %t-64.o 2>&1 | FileCheck %s + +# CHECK: Bitness is incompatible with that of the selected target + +# 32.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + +# 64.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ARCH_64] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x08 +... diff --git a/test/elf/Mips/e-flags-merge-5.test b/test/elf/Mips/e-flags-merge-5.test new file mode 100644 index 0000000000000..3b5b397ab7808 --- /dev/null +++ b/test/elf/Mips/e-flags-merge-5.test @@ -0,0 +1,42 @@ +# Check that LLD does not allow to mix 32 and 64-bit MIPS object files. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-32.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-64.o + +# RUN: not lld -flavor gnu -target mipsel -shared -o %t.so \ +# RUN: %t-32.o %t-64.o 2>&1 | FileCheck %s + +# CHECK: Bitness is incompatible with that of the selected target + +# 32.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + +# 64.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ARCH_64] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 +... diff --git a/test/elf/Mips/e-flags-merge-6-64.test b/test/elf/Mips/e-flags-merge-6-64.test new file mode 100644 index 0000000000000..fbc32b7135b20 --- /dev/null +++ b/test/elf/Mips/e-flags-merge-6-64.test @@ -0,0 +1,79 @@ +# Check selecting ELF header ARCH flag. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-m3.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-m5.o +# RUN: yaml2obj -format=elf -docnum 3 %s > %t-m64.o +# RUN: yaml2obj -format=elf -docnum 4 %s > %t-m64r2.o + +# RUN: lld -flavor gnu -target mips64el -shared -o %t.so \ +# RUN: %t-m64.o %t-m5.o %t-m64r2.o %t-m3.o +# RUN: llvm-readobj -file-headers %t.so | FileCheck %s + +# CHECK: Flags [ (0x80000000) +# CHECK-NEXT: EF_MIPS_ARCH_64R2 (0x80000000) +# CHECK-NEXT: ] + +# m3.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ARCH_3] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x08 + +# m5.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ARCH_5] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x08 + +# m64.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ARCH_64] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x08 + +# m64r2.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ARCH_64R2] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x08 +... diff --git a/test/elf/Mips/e-flags-merge-6.test b/test/elf/Mips/e-flags-merge-6.test new file mode 100644 index 0000000000000..759c8b63c97db --- /dev/null +++ b/test/elf/Mips/e-flags-merge-6.test @@ -0,0 +1,80 @@ +# Check selecting ELF header ARCH flag. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-m1.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-m2.o +# RUN: yaml2obj -format=elf -docnum 3 %s > %t-m32.o +# RUN: yaml2obj -format=elf -docnum 4 %s > %t-m32r2.o + +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so \ +# RUN: %t-m32.o %t-m2.o %t-m32r2.o %t-m1.o +# RUN: llvm-readobj -file-headers %t.so | FileCheck %s + +# CHECK: Flags [ (0x70001000) +# CHECK-NEXT: EF_MIPS_ABI_O32 (0x1000) +# CHECK-NEXT: EF_MIPS_ARCH_32R2 (0x70000000) +# CHECK-NEXT: ] + +# m1.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_1] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + +# m2.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_2] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + +# m32.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + +# m32r2.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 +... diff --git a/test/elf/Mips/e-flags-merge-7-64.test b/test/elf/Mips/e-flags-merge-7-64.test new file mode 100644 index 0000000000000..07ed6bb548366 --- /dev/null +++ b/test/elf/Mips/e-flags-merge-7-64.test @@ -0,0 +1,42 @@ +# Check that LLD does not allow to mix nan2008 and legacy MIPS object files. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-2008.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-legacy.o + +# RUN: not lld -flavor gnu -target mips64el -shared -o %t.so \ +# RUN: %t-2008.o %t-legacy.o 2>&1 | FileCheck %s + +# CHECK: Linking -mnan=2008 and -mnan=legacy modules + +# 2008.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ARCH_64, EF_MIPS_NAN2008] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x08 + +# legacy.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ARCH_64] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x08 +... diff --git a/test/elf/Mips/e-flags-merge-7.test b/test/elf/Mips/e-flags-merge-7.test new file mode 100644 index 0000000000000..7e114ff968fec --- /dev/null +++ b/test/elf/Mips/e-flags-merge-7.test @@ -0,0 +1,42 @@ +# Check that LLD does not allow to mix nan2008 and legacy MIPS object files. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-2008.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-legacy.o + +# RUN: not lld -flavor gnu -target mipsel -shared -o %t.so \ +# RUN: %t-2008.o %t-legacy.o 2>&1 | FileCheck %s + +# CHECK: Linking -mnan=2008 and -mnan=legacy modules + +# 2008.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_32, EF_MIPS_NAN2008] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + +# legacy.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 +... diff --git a/test/elf/Mips/e-flags-merge-8.test b/test/elf/Mips/e-flags-merge-8.test new file mode 100644 index 0000000000000..57af77d70260a --- /dev/null +++ b/test/elf/Mips/e-flags-merge-8.test @@ -0,0 +1,65 @@ +# Check that LLD links files with mips32 and mips64 instructions +# if all these files satisfy O32 ABI. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-32.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-64.o +# RUN: yaml2obj -format=elf -docnum 3 %s > %t-64r2.o + +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-32.o %t-64.o %t-64r2.o +# RUN: llvm-readobj -file-headers %t.so | FileCheck %s + +# CHECK: Flags [ (0x80001100) +# CHECK-NEXT: EF_MIPS_32BITMODE (0x100) +# CHECK-NEXT: EF_MIPS_ABI_O32 (0x1000) +# CHECK-NEXT: EF_MIPS_ARCH_64R2 (0x80000000) +# CHECK-NEXT: ] + + +# 32.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + +# 64.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_64, EF_MIPS_32BITMODE] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + +# 64r2.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_64R2, EF_MIPS_32BITMODE] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 +... diff --git a/test/elf/Mips/e-flags-merge-9.test b/test/elf/Mips/e-flags-merge-9.test new file mode 100644 index 0000000000000..dea32f07cb9e6 --- /dev/null +++ b/test/elf/Mips/e-flags-merge-9.test @@ -0,0 +1,43 @@ +# Check that LLD shows an error and does not link files with mips32r2 +# and mips64 instructions sets. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-32r2.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-64.o + +# RUN: not lld -flavor gnu -target mipsel -shared -o %t.so \ +# RUN: %t-32r2.o %t-64.o 2>&1 | FileCheck %s + +# CHECK: Linking modules with incompatible ISA + +# 32r2.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + +# 64.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_64, EF_MIPS_32BITMODE] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 +... diff --git a/test/elf/Mips/entry-name.test b/test/elf/Mips/entry-name.test new file mode 100644 index 0000000000000..b10adc68bde89 --- /dev/null +++ b/test/elf/Mips/entry-name.test @@ -0,0 +1,26 @@ +# Check name of executable entry symbol. +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mipsel --noinhibit-exec -o %t.exe %t.o +# RUN: llvm-nm %t.exe | FileCheck %s + +# CHECK: U __start +# CHECK: 00400108 T main + +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_ABI_O32, EF_MIPS_ARCH_32 ] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Global: + - Name: main + Section: .text diff --git a/test/elf/Mips/exe-dynamic.test b/test/elf/Mips/exe-dynamic.test new file mode 100644 index 0000000000000..28d2b13fbce87 --- /dev/null +++ b/test/elf/Mips/exe-dynamic.test @@ -0,0 +1,108 @@ +# Check MIPS specific tags in the dynamic table in case executable linking. + +# Build shared library +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o + +# Build executable +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e glob -o %t.exe %t-o.o %t.so +# RUN: llvm-readobj -dynamic-table %t.exe | FileCheck %s + +# CHECK: Format: ELF32-mips +# CHECK: Arch: mipsel +# CHECK: AddressSize: 32bit +# CHECK: LoadName: +# CHECK: DynamicSection [ (18 entries) +# CHECK: Tag Type Name/Value +# CHECK-NEXT: 0x00000004 HASH 0x400104 +# CHECK-NEXT: 0x00000005 STRTAB 0x400138 +# CHECK-NEXT: 0x00000006 SYMTAB 0x400118 +# CHECK-NEXT: 0x0000000A STRSZ 28 (bytes) +# CHECK-NEXT: 0x0000000B SYMENT 16 (bytes) +# CHECK-NEXT: 0x00000002 PLTRELSZ 8 (bytes) +# CHECK-NEXT: 0x70000032 MIPS_PLTGOT 0x402000 +# CHECK-NEXT: 0x00000014 PLTREL REL +# CHECK-NEXT: 0x00000017 JMPREL 0x400154 +# CHECK-NEXT: 0x70000001 MIPS_RLD_VERSION 1 +# CHECK-NEXT: 0x70000005 MIPS_FLAGS NOTPOT +# CHECK-NEXT: 0x70000006 MIPS_BASE_ADDRESS 0x400000 +# CHECK-NEXT: 0x7000000A MIPS_LOCAL_GOTNO 2 +# CHECK-NEXT: 0x70000011 MIPS_SYMTABNO 2 +# CHECK-NEXT: 0x70000013 MIPS_GOTSYM 0x2 +# CHECK-NEXT: 0x00000003 PLTGOT 0x401000 +# CHECK-NEXT: 0x00000001 NEEDED SharedLibrary (exe-dynamic.test.tmp.so) +# CHECK-NEXT: 0x00000000 NULL 0x0 +# CHECK-NEXT: ] + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Content: 0000000C000000000000000C000000000000000C00000000 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_26 + Addend: 0 + - Offset: 0x08 + Symbol: .text + Type: R_MIPS_26 + Addend: 0 + - Offset: 0x10 + Symbol: glob + Type: R_MIPS_26 + Addend: 0 + +Symbols: + Local: + - Name: loc + Section: .text + - Name: .text + Type: STT_SECTION + Section: .text + Global: + - Name: glob + Section: .text + Value: 0x08 + - Name: T1 +... diff --git a/test/elf/Mips/exe-dynsym-micro.test b/test/elf/Mips/exe-dynsym-micro.test new file mode 100644 index 0000000000000..e3b00277ef6a9 --- /dev/null +++ b/test/elf/Mips/exe-dynsym-micro.test @@ -0,0 +1,94 @@ +# Check that symbol referenced by an entry in the global part of GOT +# has a corresponded entry in the .dynsym section. This test covers +# the case when the GOT entry created because of the R_MICROMIPS_GOT16 +# relocation. + +# Build executable +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mipsel -e glob -o %t.exe %t.o +# RUN: llvm-readobj -dyn-symbols %t.exe | FileCheck -check-prefix=CHECK-DYN %s + +# Build executabl (yaml format)e +# RUN: lld -flavor gnu -target mipsel -e glob \ +# RUN: --output-filetype=yaml -o %t.yaml %t.o +# RUN: FileCheck -check-prefix=CHECK-GOT %s < %t.yaml + +# CHECK-DYN: Format: ELF32-mips +# CHECK-DYN: Arch: mipsel +# CHECK-DYN: AddressSize: 32bit +# CHECK-DYN: LoadName: +# CHECK-DYN: DynamicSymbols [ +# CHECK-DYN: Symbol { +# CHECK-DYN: Name: @ (0) +# CHECK-DYN: Value: 0x0 +# CHECK-DYN: Size: 0 +# CHECK-DYN: Binding: Local (0x0) +# CHECK-DYN: Type: None (0x0) +# CHECK-DYN: Other: 0 +# CHECK-DYN: Section: Undefined (0x0) +# CHECK-DYN: } +# CHECK-DYN: Symbol { +# CHECK-DYN: Name: weakf@ (1) +# CHECK-DYN: Value: 0x0 +# CHECK-DYN: Size: 0 +# CHECK-DYN: Binding: Weak (0x2) +# CHECK-DYN: Type: None (0x0) +# CHECK-DYN: Other: 0 +# CHECK-DYN: Section: Undefined (0x0) +# CHECK-DYN: } +# CHECK-DYN: ] + +# CHECK-GOT: - type: got +# CHECK-GOT: content: [ 00, 00, 00, 00 ] +# CHECK-GOT: alignment: 2^2 +# CHECK-GOT: section-choice: custom-required +# CHECK-GOT: section-name: .got +# CHECK-GOT: permissions: rw- +# CHECK-GOT: - type: got +# CHECK-GOT: content: [ 00, 00, 00, 80 ] +# CHECK-GOT: alignment: 2^2 +# CHECK-GOT: section-choice: custom-required +# CHECK-GOT: section-name: .got +# CHECK-GOT: permissions: rw- +# CHECK-GOT: - ref-name: L000 +# CHECK-GOT: type: got +# CHECK-GOT: content: [ 00, 00, 00, 00 ] +# CHECK-GOT: alignment: 2^2 +# CHECK-GOT: section-choice: custom-required +# CHECK-GOT: section-name: .got +# CHECK-GOT: permissions: rw- +# CHECK-GOT: references: +# CHECK-GOT: - kind: LLD_R_MIPS_GLOBAL_GOT +# CHECK-GOT: offset: 0 +# CHECK-GOT: target: weakf + +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x04 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: weakf + Type: R_MICROMIPS_GOT16 + +Symbols: + Global: + - Name: glob + Section: .text + Other: [ STO_MIPS_MICROMIPS ] + Weak: + - Name: weakf diff --git a/test/elf/Mips/exe-dynsym.test b/test/elf/Mips/exe-dynsym.test new file mode 100644 index 0000000000000..a59916c4be4c4 --- /dev/null +++ b/test/elf/Mips/exe-dynsym.test @@ -0,0 +1,91 @@ +# Check that symbol referenced by an entry in the global part of GOT +# has a corresponded entry in the .dynsym section. + +# Build executable +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mipsel -e glob -o %t.exe %t.o +# RUN: llvm-readobj -dyn-symbols %t.exe | FileCheck -check-prefix=CHECK-DYN %s + +# Build executabl (yaml format)e +# RUN: lld -flavor gnu -target mipsel -e glob \ +# RUN: --output-filetype=yaml -o %t.yaml %t.o +# RUN: FileCheck -check-prefix=CHECK-GOT %s < %t.yaml + +# CHECK-DYN: Format: ELF32-mips +# CHECK-DYN: Arch: mipsel +# CHECK-DYN: AddressSize: 32bit +# CHECK-DYN: LoadName: +# CHECK-DYN: DynamicSymbols [ +# CHECK-DYN: Symbol { +# CHECK-DYN: Name: @ (0) +# CHECK-DYN: Value: 0x0 +# CHECK-DYN: Size: 0 +# CHECK-DYN: Binding: Local (0x0) +# CHECK-DYN: Type: None (0x0) +# CHECK-DYN: Other: 0 +# CHECK-DYN: Section: Undefined (0x0) +# CHECK-DYN: } +# CHECK-DYN: Symbol { +# CHECK-DYN: Name: weakf@ (1) +# CHECK-DYN: Value: 0x0 +# CHECK-DYN: Size: 0 +# CHECK-DYN: Binding: Weak (0x2) +# CHECK-DYN: Type: None (0x0) +# CHECK-DYN: Other: 0 +# CHECK-DYN: Section: Undefined (0x0) +# CHECK-DYN: } +# CHECK-DYN: ] + +# CHECK-GOT: - type: got +# CHECK-GOT: content: [ 00, 00, 00, 00 ] +# CHECK-GOT: alignment: 2^2 +# CHECK-GOT: section-choice: custom-required +# CHECK-GOT: section-name: .got +# CHECK-GOT: permissions: rw- +# CHECK-GOT: - type: got +# CHECK-GOT: content: [ 00, 00, 00, 80 ] +# CHECK-GOT: alignment: 2^2 +# CHECK-GOT: section-choice: custom-required +# CHECK-GOT: section-name: .got +# CHECK-GOT: permissions: rw- +# CHECK-GOT: - ref-name: L000 +# CHECK-GOT: type: got +# CHECK-GOT: content: [ 00, 00, 00, 00 ] +# CHECK-GOT: alignment: 2^2 +# CHECK-GOT: section-choice: custom-required +# CHECK-GOT: section-name: .got +# CHECK-GOT: permissions: rw- +# CHECK-GOT: references: +# CHECK-GOT: - kind: LLD_R_MIPS_GLOBAL_GOT +# CHECK-GOT: offset: 0 +# CHECK-GOT: target: weakf + +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x04 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: weakf + Type: R_MIPS_GOT16 + +Symbols: + Global: + - Name: glob + Section: .text + Weak: + - Name: weakf diff --git a/test/elf/Mips/exe-fileheader-64.test b/test/elf/Mips/exe-fileheader-64.test new file mode 100644 index 0000000000000..63baff53e6782 --- /dev/null +++ b/test/elf/Mips/exe-fileheader-64.test @@ -0,0 +1,66 @@ +# Check ELF Header for 64-bit executable file. + +# Build executable +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target mips64el -e glob -o %t.exe %t-o.o +# RUN: llvm-readobj -file-headers %t.exe | FileCheck %s + +# CHECK: Format: ELF64-mips +# CHECK: Arch: mips64el +# CHECK: AddressSize: 64bit +# CHECK: LoadName: +# CHECK: ElfHeader { +# CHECK: Ident { +# CHECK: Magic: (7F 45 4C 46) +# CHECK: Class: 64-bit (0x2) +# CHECK: DataEncoding: LittleEndian (0x1) +# CHECK: FileVersion: 1 +# CHECK: OS/ABI: SystemV (0x0) +# CHECK: ABIVersion: 0 +# CHECK: Unused: (00 00 00 00 00 00 00) +# CHECK: } +# CHECK: Type: Executable (0x2) +# CHECK: Machine: EM_MIPS (0x8) +# CHECK: Version: 1 +# CHECK: Entry: 0x1200001A0 +# CHECK: ProgramHeaderOffset: 0x40 +# CHECK: SectionHeaderOffset: 0x1300 +# CHECK: Flags [ (0x60000007) +# CHECK: EF_MIPS_ARCH_64 (0x60000000) +# CHECK: EF_MIPS_CPIC (0x4) +# CHECK: EF_MIPS_NOREORDER (0x1) +# CHECK: EF_MIPS_PIC (0x2) +# CHECK: ] +# CHECK: HeaderSize: 64 +# CHECK: ProgramHeaderEntrySize: 56 +# CHECK: ProgramHeaderCount: 5 +# CHECK: SectionHeaderEntrySize: 64 +# CHECK: SectionHeaderCount: 11 +# CHECK: StringTableSectionIndex: 8 +# CHECK: } + +# o.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ARCH_64 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x08 + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + Global: + - Name: glob + Section: .text +... diff --git a/test/elf/Mips/exe-fileheader-micro-64.test b/test/elf/Mips/exe-fileheader-micro-64.test new file mode 100644 index 0000000000000..044c2f729f388 --- /dev/null +++ b/test/elf/Mips/exe-fileheader-micro-64.test @@ -0,0 +1,68 @@ +# Check ELF Header for 64-bit executable file in case of microMIPS entry symbol. + +# Build executable +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target mips64el -e glob -o %t.exe %t-o.o +# RUN: llvm-readobj -file-headers %t.exe | FileCheck %s + +# CHECK: Format: ELF64-mips +# CHECK: Arch: mips64el +# CHECK: AddressSize: 64bit +# CHECK: LoadName: +# CHECK: ElfHeader { +# CHECK: Ident { +# CHECK: Magic: (7F 45 4C 46) +# CHECK: Class: 64-bit (0x2) +# CHECK: DataEncoding: LittleEndian (0x1) +# CHECK: FileVersion: 1 +# CHECK: OS/ABI: SystemV (0x0) +# CHECK: ABIVersion: 0 +# CHECK: Unused: (00 00 00 00 00 00 00) +# CHECK: } +# CHECK: Type: Executable (0x2) +# CHECK: Machine: EM_MIPS (0x8) +# CHECK: Version: 1 +# CHECK: Entry: 0x1200001A1 +# CHECK: ProgramHeaderOffset: 0x40 +# CHECK: SectionHeaderOffset: 0x1300 +# CHECK: Flags [ (0x82000007) +# CHECK: EF_MIPS_ARCH_64R2 (0x80000000) +# CHECK: EF_MIPS_CPIC (0x4) +# CHECK: EF_MIPS_MICROMIPS (0x2000000) +# CHECK: EF_MIPS_NOREORDER (0x1) +# CHECK: EF_MIPS_PIC (0x2) +# CHECK: ] +# CHECK: HeaderSize: 64 +# CHECK: ProgramHeaderEntrySize: 56 +# CHECK: ProgramHeaderCount: 5 +# CHECK: SectionHeaderEntrySize: 64 +# CHECK: SectionHeaderCount: 11 +# CHECK: StringTableSectionIndex: 8 +# CHECK: } + +# o.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_MICROMIPS, EF_MIPS_ARCH_64R2 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x08 + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + Global: + - Name: glob + Section: .text + Other: [ STO_MIPS_MICROMIPS ] +... diff --git a/test/elf/Mips/exe-fileheader-micro.test b/test/elf/Mips/exe-fileheader-micro.test new file mode 100644 index 0000000000000..351f299b04cdc --- /dev/null +++ b/test/elf/Mips/exe-fileheader-micro.test @@ -0,0 +1,69 @@ +# Check ELF Header for non-pic executable file in case +# of microMIPS entry symbol. + +# Build executable +# RUN: yaml2obj -format=elf %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e glob -o %t.exe %t-o.o +# RUN: llvm-readobj -file-headers %t.exe | FileCheck %s + +# CHECK: Format: ELF32-mips +# CHECK-NEXT: Arch: mipsel +# CHECK-NEXT: AddressSize: 32bit +# CHECK-NEXT: LoadName: +# CHECK-NEXT: ElfHeader { +# CHECK-NEXT: Ident { +# CHECK-NEXT: Magic: (7F 45 4C 46) +# CHECK-NEXT: Class: 32-bit (0x1) +# CHECK-NEXT: DataEncoding: LittleEndian (0x1) +# CHECK-NEXT: FileVersion: 1 +# CHECK-NEXT: OS/ABI: SystemV (0x0) +# CHECK-NEXT: ABIVersion: 0 +# CHECK-NEXT: Unused: (00 00 00 00 00 00 00) +# CHECK-NEXT: } +# CHECK-NEXT: Type: Executable (0x2) +# CHECK-NEXT: Machine: EM_MIPS (0x8) +# CHECK-NEXT: Version: 1 +# CHECK-NEXT: Entry: 0x400109 +# CHECK-NEXT: ProgramHeaderOffset: 0x34 +# CHECK-NEXT: SectionHeaderOffset: 0x1268 +# CHECK-NEXT: Flags [ (0x72001005) +# CHECK-NEXT: EF_MIPS_ABI_O32 (0x1000) +# CHECK-NEXT: EF_MIPS_ARCH_32R2 (0x70000000) +# CHECK-NEXT: EF_MIPS_CPIC (0x4) +# CHECK-NEXT: EF_MIPS_MICROMIPS (0x2000000) +# CHECK-NEXT: EF_MIPS_NOREORDER (0x1) +# CHECK-NEXT: ] +# CHECK-NEXT: HeaderSize: 52 +# CHECK-NEXT: ProgramHeaderEntrySize: 32 +# CHECK-NEXT: ProgramHeaderCount: 5 +# CHECK-NEXT: SectionHeaderEntrySize: 40 +# CHECK-NEXT: SectionHeaderCount: 11 +# CHECK-NEXT: StringTableSectionIndex: 8 +# CHECK-NEXT: } + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x08 + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + Global: + - Name: glob + Section: .text + Other: [ STO_MIPS_MICROMIPS ] +... diff --git a/test/elf/Mips/exe-fileheader.test b/test/elf/Mips/exe-fileheader.test new file mode 100644 index 0000000000000..ff0d38198c316 --- /dev/null +++ b/test/elf/Mips/exe-fileheader.test @@ -0,0 +1,105 @@ +# Check ELF Header for non-pic executable file. + +# Build shared library +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o + +# Build executable +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e glob -o %t.exe %t-o.o %t.so +# RUN: llvm-readobj -file-headers %t.exe | FileCheck %s + +# CHECK: Format: ELF32-mips +# CHECK: Arch: mipsel +# CHECK: AddressSize: 32bit +# CHECK: LoadName: +# CHECK: ElfHeader { +# CHECK: Ident { +# CHECK: Magic: (7F 45 4C 46) +# CHECK: Class: 32-bit (0x1) +# CHECK: DataEncoding: LittleEndian (0x1) +# CHECK: FileVersion: 1 +# CHECK: OS/ABI: SystemV (0x0) +# CHECK: ABIVersion: 1 +# CHECK: Unused: (00 00 00 00 00 00 00) +# CHECK: } +# CHECK: Type: Executable (0x2) +# CHECK: Machine: EM_MIPS (0x8) +# CHECK: Version: 1 +# CHECK: Entry: 0x400190 +# CHECK: ProgramHeaderOffset: 0x34 +# CHECK: SectionHeaderOffset: 0x2280 +# CHECK: Flags [ (0x70001005) +# CHECK: EF_MIPS_ABI_O32 (0x1000) +# CHECK: EF_MIPS_ARCH_32R2 (0x70000000) +# CHECK: EF_MIPS_CPIC (0x4) +# CHECK: EF_MIPS_NOREORDER (0x1) +# CHECK: ] +# CHECK: HeaderSize: 52 +# CHECK: ProgramHeaderEntrySize: 32 +# CHECK: ProgramHeaderCount: 6 +# CHECK: SectionHeaderEntrySize: 40 +# CHECK: SectionHeaderCount: 14 +# CHECK: StringTableSectionIndex: 11 +# CHECK: } + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x08 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_26 + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + Global: + - Name: glob + Section: .text + - Name: T1 +... diff --git a/test/elf/Mips/exe-got-micro.test b/test/elf/Mips/exe-got-micro.test new file mode 100644 index 0000000000000..d2d1588ab964b --- /dev/null +++ b/test/elf/Mips/exe-got-micro.test @@ -0,0 +1,115 @@ +# Check that external symbol defined in the executable file +# and referenced by R_MICROMIPS_CALL16 relocation has a corresponded +# entry in the local GOT section. +# +# Build shared library +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o + +# Build executable +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e glob \ +# RUN: --output-filetype=yaml -o %t.exe %t-o.o %t.so +# RUN: FileCheck -check-prefix=GOT %s < %t.exe + +# GOT header +# GOT: - type: got +# GOT: content: [ 00, 00, 00, 00 ] +# GOT: alignment: 2^2 +# GOT: section-choice: custom-required +# GOT: section-name: .got +# GOT: permissions: rw- +# GOT: - type: got +# GOT: content: [ 00, 00, 00, 80 ] +# GOT: alignment: 2^2 +# GOT: section-choice: custom-required +# GOT: section-name: .got +# GOT: permissions: rw- +# Local GOT entry for 'glob' symbol +# GOT: - ref-name: L000 +# GOT: type: got +# GOT: content: [ 00, 00, 00, 00 ] +# GOT: alignment: 2^2 +# GOT: section-choice: custom-required +# GOT: section-name: .got +# GOT: permissions: rw- +# GOT: references: +# GOT: - kind: R_MIPS_32 +# GOT: offset: 0 +# GOT: target: glob +# Global GOT entry for 'T1' symbol +# GOT: - ref-name: L001 +# GOT: type: got +# GOT: content: [ 00, 00, 00, 00 ] +# GOT: alignment: 2^2 +# GOT: section-choice: custom-required +# GOT: section-name: .got +# GOT: permissions: rw- +# GOT: references: +# GOT: - kind: LLD_R_MIPS_GLOBAL_GOT +# GOT: offset: 0 +# GOT: target: T1 + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x08 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: glob + Type: R_MICROMIPS_CALL16 + - Offset: 0x04 + Symbol: T1 + Type: R_MICROMIPS_CALL16 + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + Global: + - Name: glob + Section: .text + Other: [ STO_MIPS_MICROMIPS ] + - Name: T1 +... diff --git a/test/elf/Mips/exe-got.test b/test/elf/Mips/exe-got.test new file mode 100644 index 0000000000000..7254c87530bcb --- /dev/null +++ b/test/elf/Mips/exe-got.test @@ -0,0 +1,116 @@ +# Check that external symbol defined in the executable file +# and referenced by R_MIPS_CALL16 relocation has a corresponded +# entry in the local GOT section. +# +# Build shared library +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o + +# Build executable +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e glob \ +# RUN: --output-filetype=yaml -o %t.exe %t-o.o %t.so +# RUN: FileCheck -check-prefix=GOT %s < %t.exe + +# GOT header +# GOT: - type: got +# GOT: content: [ 00, 00, 00, 00 ] +# GOT: alignment: 2^2 +# GOT: section-choice: custom-required +# GOT: section-name: .got +# GOT: permissions: rw- +# GOT: - type: got +# GOT: content: [ 00, 00, 00, 80 ] +# GOT: alignment: 2^2 +# GOT: section-choice: custom-required +# GOT: section-name: .got +# GOT: permissions: rw- +# Local GOT entry for 'glob' symbol +# GOT: - ref-name: L000 +# GOT: type: got +# GOT: content: [ 00, 00, 00, 00 ] +# GOT: alignment: 2^2 +# GOT: section-choice: custom-required +# GOT: section-name: .got +# GOT: permissions: rw- +# GOT: references: +# GOT: - kind: R_MIPS_32 +# GOT: offset: 0 +# GOT: target: glob +# Global GOT entry for 'T1' symbol +# GOT: - ref-name: L001 +# GOT: type: got +# GOT: content: [ 00, 00, 00, 00 ] +# GOT: alignment: 2^2 +# GOT: section-choice: custom-required +# GOT: section-name: .got +# GOT: permissions: rw- +# GOT: references: +# GOT: - kind: LLD_R_MIPS_GLOBAL_GOT +# GOT: offset: 0 +# GOT: target: T1 + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x08 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: glob + Type: R_MIPS_CALL16 + Addend: 0 + - Offset: 0x04 + Symbol: T1 + Type: R_MIPS_CALL16 + Addend: 0 + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + Global: + - Name: glob + Section: .text + - Name: T1 +... diff --git a/test/elf/Mips/got-page-32.test b/test/elf/Mips/got-page-32.test new file mode 100644 index 0000000000000..00376da786633 --- /dev/null +++ b/test/elf/Mips/got-page-32.test @@ -0,0 +1,203 @@ +# Check handling of R_MIPS_GOT_DISP / PAGE / OFST relocations. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o.o %t.so +# RUN: llvm-readobj -symbols -dyn-symbols -mips-plt-got %t.exe \ +# RUN: | FileCheck -check-prefix=GOT %s +# RUN: llvm-objdump -s %t.exe | FileCheck -check-prefix=RAW %s + +# GOT: Symbol { +# GOT: Name: T0 (1) +# GOT-NEXT: Value: 0x400154 +# GOT: Symbol { +# GOT: Name: LT1 (4) +# GOT-NEXT: Value: 0x40017C +# GOT: Symbol { +# GOT: Name: LT2 (8) +# GOT-NEXT: Value: 0x400180 +# GOT: Symbol { +# GOT: Name: T1@ (1) +# GOT-NEXT: Value: 0x0 +# GOT: Symbol { +# GOT: Name: T2@ (4) +# GOT-NEXT: Value: 0x0 + +# GOT: Primary GOT { +# GOT-NEXT: Canonical gp value: 0x408FF0 +# GOT-NEXT: Reserved entries [ +# GOT-NEXT: Entry { +# GOT-NEXT: Address: 0x401000 +# GOT-NEXT: Access: -32752 +# GOT-NEXT: Initial: 0x0 +# GOT-NEXT: Purpose: Lazy resolver +# GOT-NEXT: } +# GOT-NEXT: Entry { +# GOT-NEXT: Address: 0x401004 +# GOT-NEXT: Access: -32748 +# GOT-NEXT: Initial: 0x80000000 +# GOT-NEXT: Purpose: Module pointer (GNU extension) +# GOT-NEXT: } +# GOT-NEXT: ] +# GOT-NEXT: Local entries [ +# GOT-NEXT: Entry { +# GOT-NEXT: Address: 0x401008 +# GOT-NEXT: Access: -32744 +# GOT-NEXT: Initial: 0x40017C +# GOT-NEXT: } +# GOT-NEXT: Entry { +# GOT-NEXT: Address: 0x40100C +# GOT-NEXT: Access: -32740 +# GOT-NEXT: Initial: 0x400000 +# GOT-NEXT: } +# GOT-NEXT: Entry { +# GOT-NEXT: Address: 0x401010 +# GOT-NEXT: Access: -32736 +# GOT-NEXT: Initial: 0x400000 +# GOT-NEXT: } +# GOT-NEXT: ] +# GOT-NEXT: Global entries [ +# GOT-NEXT: Entry { +# GOT-NEXT: Address: 0x401014 +# GOT-NEXT: Access: -32732 +# GOT-NEXT: Initial: 0x0 +# GOT-NEXT: Value: 0x0 +# GOT-NEXT: Type: Function (0x2) +# GOT-NEXT: Section: Undefined (0x0) +# GOT-NEXT: Name: T1@ (1) +# GOT-NEXT: } +# GOT-NEXT: Entry { +# GOT-NEXT: Address: 0x401018 +# GOT-NEXT: Access: -32728 +# GOT-NEXT: Initial: 0x0 +# GOT-NEXT: Value: 0x0 +# GOT-NEXT: Type: Function (0x2) +# GOT-NEXT: Section: Undefined (0x0) +# GOT-NEXT: Name: T2@ (4) +# GOT-NEXT: } +# GOT-NEXT: ] +# GOT-NEXT: Number of TLS and multi-GOT entries: 0 +# GOT-NEXT: } + +# RAW: Contents of section .text: +# RAW-NEXT: 400154 24800000 18800000 24800000 28800000 $.......$...(... +# ^ = -32732 (T1) +# ^ = -32744 (LT1) +# ^ -32732 (T1) +# ^ -32728 (T2) +# RAW-NEXT: 400164 1c800000 20800000 00000000 00000000 .... ........... +# ^ -32740 (PAGE) +# ^ -32736 (PAGE) +# ^ T1 OFST +# ^ T2 OFST +# RAW-NEXT: 400174 7c010000 80010000 00000000 00000000 |............... +# ^ LT1 OFST +# ^ LT2 OFST + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ARCH_32, EF_MIPS_ABI_O32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 8 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 0x4 + - Name: T2 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 0x4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ARCH_32, EF_MIPS_ABI_O32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x4 + Size: 0x30 + + - Name: .rel.text + Type: SHT_RELA + Link: .symtab + Info: .text + AddressAlign: 0x04 + Relocations: + - Offset: 0x0 + Symbol: T1 + Type: R_MIPS_GOT_DISP + - Offset: 0x4 + Symbol: LT1 + Type: R_MIPS_GOT_DISP + - Offset: 0x8 + Symbol: T1 + Type: R_MIPS_GOT_PAGE + - Offset: 0xC + Symbol: T2 + Type: R_MIPS_GOT_PAGE + - Offset: 0x10 + Symbol: LT1 + Type: R_MIPS_GOT_PAGE + - Offset: 0x14 + Symbol: LT2 + Type: R_MIPS_GOT_PAGE + - Offset: 0x18 + Symbol: T1 + Type: R_MIPS_GOT_OFST + - Offset: 0x1C + Symbol: T2 + Type: R_MIPS_GOT_OFST + - Offset: 0x20 + Symbol: LT1 + Type: R_MIPS_GOT_OFST + - Offset: 0x24 + Symbol: LT2 + Type: R_MIPS_GOT_OFST + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + + Global: + - Name: T0 + Type: STT_FUNC + Section: .text + Size: 0x8 + - Name: LT1 + Type: STT_FUNC + Section: .text + Value: 0x28 + Size: 0x4 + - Name: LT2 + Type: STT_FUNC + Section: .text + Value: 0x2c + Size: 0x4 + - Name: T1 + - Name: T2 +... diff --git a/test/elf/Mips/got-page-64.test b/test/elf/Mips/got-page-64.test new file mode 100644 index 0000000000000..21bece5d32420 --- /dev/null +++ b/test/elf/Mips/got-page-64.test @@ -0,0 +1,203 @@ +# Check handling of R_MIPS_GOT_DISP / PAGE / OFST relocations. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mips64el -shared -o %t.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mips64el -e T0 -o %t.exe %t-o.o %t.so +# RUN: llvm-readobj -symbols -dyn-symbols -mips-plt-got %t.exe \ +# RUN: | FileCheck -check-prefix=GOT %s +# RUN: llvm-objdump -s %t.exe | FileCheck -check-prefix=RAW %s + +# GOT: Symbol { +# GOT: Name: T0 (1) +# GOT-NEXT: Value: 0x1200001F0 +# GOT: Symbol { +# GOT: Name: LT1 (4) +# GOT-NEXT: Value: 0x120000218 +# GOT: Symbol { +# GOT: Name: LT2 (8) +# GOT-NEXT: Value: 0x12000021C +# GOT: Symbol { +# GOT: Name: T1@ (1) +# GOT-NEXT: Value: 0x0 +# GOT: Symbol { +# GOT: Name: T2@ (4) +# GOT-NEXT: Value: 0x0 + +# GOT: Primary GOT { +# GOT-NEXT: Canonical gp value: 0x120008FF0 +# GOT-NEXT: Reserved entries [ +# GOT-NEXT: Entry { +# GOT-NEXT: Address: 0x120001000 +# GOT-NEXT: Access: -32752 +# GOT-NEXT: Initial: 0x0 +# GOT-NEXT: Purpose: Lazy resolver +# GOT-NEXT: } +# GOT-NEXT: Entry { +# GOT-NEXT: Address: 0x120001008 +# GOT-NEXT: Access: -32744 +# GOT-NEXT: Initial: 0x8000000000000000 +# GOT-NEXT: Purpose: Module pointer (GNU extension) +# GOT-NEXT: } +# GOT-NEXT: ] +# GOT-NEXT: Local entries [ +# GOT-NEXT: Entry { +# GOT-NEXT: Address: 0x120001010 +# GOT-NEXT: Access: -32736 +# GOT-NEXT: Initial: 0x120000218 +# GOT-NEXT: } +# GOT-NEXT: Entry { +# GOT-NEXT: Address: 0x120001018 +# GOT-NEXT: Access: -32728 +# GOT-NEXT: Initial: 0x120000000 +# GOT-NEXT: } +# GOT-NEXT: Entry { +# GOT-NEXT: Address: 0x120001020 +# GOT-NEXT: Access: -32720 +# GOT-NEXT: Initial: 0x120000000 +# GOT-NEXT: } +# GOT-NEXT: ] +# GOT-NEXT: Global entries [ +# GOT-NEXT: Entry { +# GOT-NEXT: Address: 0x120001028 +# GOT-NEXT: Access: -32712 +# GOT-NEXT: Initial: 0x0 +# GOT-NEXT: Value: 0x0 +# GOT-NEXT: Type: Function (0x2) +# GOT-NEXT: Section: Undefined (0x0) +# GOT-NEXT: Name: T1@ (1) +# GOT-NEXT: } +# GOT-NEXT: Entry { +# GOT-NEXT: Address: 0x120001030 +# GOT-NEXT: Access: -32704 +# GOT-NEXT: Initial: 0x0 +# GOT-NEXT: Value: 0x0 +# GOT-NEXT: Type: Function (0x2) +# GOT-NEXT: Section: Undefined (0x0) +# GOT-NEXT: Name: T2@ (4) +# GOT-NEXT: } +# GOT-NEXT: ] +# GOT-NEXT: Number of TLS and multi-GOT entries: 0 +# GOT-NEXT: } + +# RAW: Contents of section .text: +# RAW-NEXT: 1200001f0 38800000 20800000 38800000 40800000 8... ...8...@... +# ^ = -32712 (T1) +# ^ = -32736 (LT1) +# ^ -32712 (T1) +# ^ -32704 (T2) +# RAW-NEXT: 120000200 28800000 30800000 00000000 00000000 (...0........... +# ^ -32728 (PAGE) +# ^ -32720 (PAGE) +# ^ T1 OFST +# ^ T2 OFST +# RAW-NEXT: 120000210 18020000 1c020000 00000000 00000000 ................ +# ^ LT1 OFST +# ^ LT2 OFST + +# so.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ARCH_64] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 8 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 0x4 + - Name: T2 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 0x4 + +# o.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ARCH_64] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x4 + Size: 0x30 + + - Name: .rel.text + Type: SHT_RELA + Link: .symtab + Info: .text + AddressAlign: 0x04 + Relocations: + - Offset: 0x0 + Symbol: T1 + Type: R_MIPS_GOT_DISP + - Offset: 0x4 + Symbol: LT1 + Type: R_MIPS_GOT_DISP + - Offset: 0x8 + Symbol: T1 + Type: R_MIPS_GOT_PAGE + - Offset: 0xC + Symbol: T2 + Type: R_MIPS_GOT_PAGE + - Offset: 0x10 + Symbol: LT1 + Type: R_MIPS_GOT_PAGE + - Offset: 0x14 + Symbol: LT2 + Type: R_MIPS_GOT_PAGE + - Offset: 0x18 + Symbol: T1 + Type: R_MIPS_GOT_OFST + - Offset: 0x1C + Symbol: T2 + Type: R_MIPS_GOT_OFST + - Offset: 0x20 + Symbol: LT1 + Type: R_MIPS_GOT_OFST + - Offset: 0x24 + Symbol: LT2 + Type: R_MIPS_GOT_OFST + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + + Global: + - Name: T0 + Type: STT_FUNC + Section: .text + Size: 0x8 + - Name: LT1 + Type: STT_FUNC + Section: .text + Value: 0x28 + Size: 0x4 + - Name: LT2 + Type: STT_FUNC + Section: .text + Value: 0x2c + Size: 0x4 + - Name: T1 + - Name: T2 +... diff --git a/test/elf/Mips/got16-2.test b/test/elf/Mips/got16-2.test new file mode 100644 index 0000000000000..6f576536c5410 --- /dev/null +++ b/test/elf/Mips/got16-2.test @@ -0,0 +1,73 @@ +# Check handling of R_MIPS_GOT16 relocation against local +# symbols when addresses of local data cross 64 KBytes border. + +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t-exe %t-obj +# RUN: llvm-objdump -s %t-exe | FileCheck %s + +# CHECK: Contents of section .got: +# CHECK-NEXT: 40a000 00000000 00000080 00004000 00004100 ..........@...A. +# lazy module 0x400000 0x410000 +# resolver pointer for L1 for L2 + +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Content: '00000000000000000000000000000000' + + - Name: .rel.text + Type: SHT_REL + Link: .symtab + Info: .text + AddressAlign: 0x04 + Address: 0x1000 + Relocations: + - Offset: 0 + Symbol: L1 + Type: R_MIPS_GOT16 + - Offset: 4 + Symbol: L1 + Type: R_MIPS_LO16 + - Offset: 8 + Symbol: L2 + Type: R_MIPS_GOT16 + - Offset: 12 + Symbol: L2 + Type: R_MIPS_LO16 + + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x04 + Size: 0x9000 + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: L1 + Type: STT_OBJECT + Section: .data + Value: 0x00 + Size: 0x8000 + - Name: L2 + Type: STT_OBJECT + Section: .data + Value: 0x8000 + Size: 0x04 + + Global: + - Name: T0 + Type: STT_FUNC + Section: .text + Size: 0x04 diff --git a/test/elf/Mips/got16-micro.test b/test/elf/Mips/got16-micro.test new file mode 100644 index 0000000000000..6b77613581ec5 --- /dev/null +++ b/test/elf/Mips/got16-micro.test @@ -0,0 +1,165 @@ +# REQUIRES: mips + +# Check handling of global/local R_MICROMIPS_GOT16 relocations. +# RUN: llvm-mc -triple=mipsel -mattr=micromips -relocation-model=pic \ +# RUN: -filetype=obj -o=%t.o %s +# RUN: lld -flavor gnu -target mipsel -shared --noinhibit-exec \ +# RUN: --output-filetype=yaml %t.o \ +# RUN: | FileCheck -check-prefix YAML %s +# RUN: lld -flavor gnu -target mipsel -shared --noinhibit-exec -o %t2 %t.o +# RUN: llvm-objdump -t -disassemble -mattr=micromips %t2 \ +# RUN: | FileCheck -check-prefix RAW %s + +# Function glob +# YAML: - name: main +# YAML: scope: global +# YAML: content: [ 5C, FC, 00, 00, 42, 30, 00, 00, 5C, FC, 00, 00, +# YAML: 42, 30, 00, 00, 5C, FC, 00, 00, 5C, FC, 00, 00, +# YAML: 5C, FC, 00, 00 ] +# YAML: alignment: 4 mod 2^4 +# YAML: code-model: mips-micro +# YAML: references: +# YAML-NEXT: - kind: R_MICROMIPS_GOT16 +# YAML-NEXT: offset: 0 +# YAML-NEXT: target: L000 +# YAML-NEXT: - kind: R_MICROMIPS_LO16 +# YAML-NEXT: offset: 4 +# YAML-NEXT: target: data_1 +# YAML-NEXT: - kind: R_MICROMIPS_GOT16 +# YAML-NEXT: offset: 8 +# YAML-NEXT: target: L001 +# YAML-NEXT: - kind: R_MICROMIPS_LO16 +# YAML-NEXT: offset: 12 +# YAML-NEXT: target: data_2 +# YAML-NEXT: - kind: R_MICROMIPS_GOT16 +# YAML-NEXT: offset: 16 +# YAML-NEXT: target: L002 +# YAML-NEXT: - kind: R_MICROMIPS_CALL16 +# YAML-NEXT: offset: 20 +# YAML-NEXT: target: L003 +# YAML-NEXT: - kind: R_MICROMIPS_CALL16 +# YAML-NEXT: offset: 24 +# YAML-NEXT: target: L004 + +# Local GOT entries: +# YAML: - ref-name: L000 +# YAML-NEXT: type: got +# YAML-NEXT: content: [ 00, 00, 00, 00 ] +# YAML-NEXT: alignment: 2^2 +# YAML-NEXT: section-choice: custom-required +# YAML-NEXT: section-name: .got +# YAML-NEXT: permissions: rw- +# YAML-NEXT: references: +# YAML-NEXT: - kind: LLD_R_MIPS_32_HI16 +# YAML-NEXT: offset: 0 +# YAML-NEXT: target: data_1 +# YAML-NEXT: - ref-name: L001 +# YAML-NEXT: type: got +# YAML-NEXT: content: [ 00, 00, 00, 00 ] +# YAML-NEXT: alignment: 2^2 +# YAML-NEXT: section-choice: custom-required +# YAML-NEXT: section-name: .got +# YAML-NEXT: permissions: rw- +# YAML-NEXT: references: +# YAML-NEXT: - kind: LLD_R_MIPS_32_HI16 +# YAML-NEXT: offset: 0 +# YAML-NEXT: target: data_2 +# YAML-NEXT: - ref-name: L002 +# YAML-NEXT: type: got +# YAML-NEXT: content: [ 00, 00, 00, 00 ] +# YAML-NEXT: alignment: 2^2 +# YAML-NEXT: section-choice: custom-required +# YAML-NEXT: section-name: .got +# YAML-NEXT: permissions: rw- +# YAML-NEXT: references: +# YAML-NEXT: - kind: R_MIPS_32 +# YAML-NEXT: offset: 0 +# YAML-NEXT: target: data_h + +# Global GOT entries: +# YAML-NEXT: - ref-name: L003 +# YAML-NEXT: type: got +# YAML-NEXT: content: [ 00, 00, 00, 00 ] +# YAML-NEXT: alignment: 2^2 +# YAML-NEXT: section-choice: custom-required +# YAML-NEXT: section-name: .got +# YAML-NEXT: permissions: rw- +# YAML-NEXT: references: +# YAML-NEXT: - kind: LLD_R_MIPS_GLOBAL_GOT +# YAML-NEXT: offset: 0 +# YAML-NEXT: target: bar +# YAML-NEXT: - kind: R_MIPS_32 +# YAML-NEXT: offset: 0 +# YAML-NEXT: target: bar +# YAML-NEXT: - ref-name: L004 +# YAML-NEXT: type: got +# YAML-NEXT: content: [ 00, 00, 00, 00 ] +# YAML-NEXT: alignment: 2^2 +# YAML-NEXT: section-choice: custom-required +# YAML-NEXT: section-name: .got +# YAML-NEXT: permissions: rw- +# YAML-NEXT: references: +# YAML-NEXT: - kind: LLD_R_MIPS_GLOBAL_GOT +# YAML-NEXT: offset: 0 +# YAML-NEXT: target: foo + +# RAW: Disassembly of section .text: +# RAW: main: +# RAW-NEXT: {{[0x0-9a-f]+}}: 5c fc 18 80 lw $2, -32744($gp) +# RAW-NEXT: {{[0x0-9a-f]+}}: 42 30 40 10 addiu $2, $2, 4160 +# RAW-NEXT: {{[0x0-9a-f]+}}: 5c fc 1c 80 lw $2, -32740($gp) +# RAW-NEXT: {{[0x0-9a-f]+}}: 42 30 60 20 addiu $2, $2, 8288 +# RAW-NEXT: {{[0x0-9a-f]+}}: 5c fc 20 80 lw $2, -32736($gp) +# RAW-NEXT: {{[0x0-9a-f]+}}: 5c fc 24 80 lw $2, -32732($gp) +# RAW-NEXT: {{[0x0-9a-f]+}}: 5c fc 28 80 lw $2, -32728($gp) + +# RAW: SYMBOL TABLE: +# RAW: {{[0x0-9a-f]+}} *UND* 00000000 +# RAW: {{[0x0-9a-f]+}} l .data 00000000 data_1 +# RAW: {{[0x0-9a-f]+}} l .data 00000001 data_2 +# RAW: {{[0x0-9a-f]+}} g F .text 00000004 bar +# RAW: {{[0x0-9a-f]+}} g F .text 0000001c main +# RAW: {{[0x0-9a-f]+}} g .data 00000001 data_h + + .data + .type data_1, @object + .size data_1, 4128 +data_1: + .byte 1 + .space 4127 + .type data_2, @object + .size data_2, 1 +data_2: + .byte 2 + .hidden data_h + .globl data_h + .type data_h, @object + .size data_h, 1 +data_h: + .byte 3 + + .text + .globl bar + .set micromips + .ent bar + .type bar, @function +bar: + nop + .end bar + .size bar, .-bar + + .globl main + .set micromips + .ent main + .type main, @function +main: + lw $2,%got(data_1)($28) + addiu $2,$2,%lo(data_1) + lw $2,%got(data_2)($28) + addiu $2,$2,%lo(data_2) + lw $2,%got(data_h)($28) + lw $2,%call16(bar)($28) + lw $2,%call16(foo)($28) + + .end main + .size main, .-main diff --git a/test/elf/Mips/got16.test b/test/elf/Mips/got16.test new file mode 100644 index 0000000000000..9090d3003c142 --- /dev/null +++ b/test/elf/Mips/got16.test @@ -0,0 +1,196 @@ +# REQUIRES: mips + +# Check handling of global/local GOT16 relocations. +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mipsel -shared --noinhibit-exec \ +# RUN: --output-filetype=yaml %t.o \ +# RUN: | FileCheck -check-prefix YAML %s +# RUN: lld -flavor gnu -target mipsel -shared --noinhibit-exec -o %t2 %t.o +# RUN: llvm-objdump -t -disassemble %t2 | FileCheck -check-prefix RAW %s + +# Function glob +# YAML: - name: glob +# YAML: scope: global +# YAML: content: [ 00, 00, 84, 8F, 00, 00, 84, 24, 01, 00, 84, 8F, +# YAML: 00, 02, 84, 24, 00, 00, 84, 8F, 00, 00, 84, 8F, +# YAML: 00, 00, 84, 8F ] +# YAML: alignment: 2^2 +# YAML: references: +# YAML: - kind: R_MIPS_GOT16 +# YAML: offset: 0 +# YAML: target: L000 +# YAML: - kind: R_MIPS_LO16 +# YAML: offset: 4 +# YAML: target: L009 +# YAML: - kind: R_MIPS_GOT16 +# YAML: offset: 8 +# YAML: target: L002 +# YAML: addend: 66048 +# YAML: - kind: R_MIPS_LO16 +# YAML: offset: 12 +# YAML: target: L009 +# YAML: addend: 512 +# YAML: - kind: R_MIPS_GOT16 +# YAML: offset: 16 +# YAML: target: L004 +# YAML: - kind: R_MIPS_CALL16 +# YAML: offset: 20 +# YAML: target: L005 +# YAML: - kind: R_MIPS_CALL16 +# YAML: offset: 24 +# YAML: target: L006 + +# Local GOT entries: +# YAML: - ref-name: L000 +# YAML-NEXT: type: got +# YAML-NEXT: content: [ 00, 00, 00, 00 ] +# YAML-NEXT: alignment: 2^2 +# YAML-NEXT: section-choice: custom-required +# YAML-NEXT: section-name: .got +# YAML-NEXT: permissions: rw- +# YAML-NEXT: references: +# YAML-NEXT: - kind: LLD_R_MIPS_32_HI16 +# YAML-NEXT: offset: 0 +# YAML-NEXT: target: L009 +# YAML-NEXT: - ref-name: L002 +# YAML-NEXT: type: got +# YAML-NEXT: content: [ 00, 00, 00, 00 ] +# YAML-NEXT: alignment: 2^2 +# YAML-NEXT: section-choice: custom-required +# YAML-NEXT: section-name: .got +# YAML-NEXT: permissions: rw- +# YAML-NEXT: references: +# YAML-NEXT: - kind: LLD_R_MIPS_32_HI16 +# YAML-NEXT: offset: 0 +# YAML-NEXT: target: L009 +# YAML-NEXT: addend: 66048 +# YAML-NEXT: - ref-name: L004 +# YAML-NEXT: type: got +# YAML-NEXT: content: [ 00, 00, 00, 00 ] +# YAML-NEXT: alignment: 2^2 +# YAML-NEXT: section-choice: custom-required +# YAML-NEXT: section-name: .got +# YAML-NEXT: permissions: rw- +# YAML-NEXT: references: +# YAML-NEXT: - kind: R_MIPS_32 +# YAML-NEXT: offset: 0 +# YAML-NEXT: target: hidden + +# Global GOT entries: +# YAML-NEXT: - ref-name: L005 +# YAML-NEXT: type: got +# YAML-NEXT: content: [ 00, 00, 00, 00 ] +# YAML-NEXT: alignment: 2^2 +# YAML-NEXT: section-choice: custom-required +# YAML-NEXT: section-name: .got +# YAML-NEXT: permissions: rw- +# YAML-NEXT: references: +# YAML-NEXT: - kind: LLD_R_MIPS_GLOBAL_GOT +# YAML-NEXT: offset: 0 +# YAML-NEXT: target: glob +# YAML-NEXT: - kind: R_MIPS_32 +# YAML-NEXT: offset: 0 +# YAML-NEXT: target: glob +# YAML-NEXT: - ref-name: L006 +# YAML-NEXT: type: got +# YAML-NEXT: content: [ 00, 00, 00, 00 ] +# YAML-NEXT: alignment: 2^2 +# YAML-NEXT: section-choice: custom-required +# YAML-NEXT: section-name: .got +# YAML-NEXT: permissions: rw- +# YAML-NEXT: references: +# YAML-NEXT: - kind: LLD_R_MIPS_GLOBAL_GOT +# YAML-NEXT: offset: 0 +# YAML-NEXT: target: extern + +# RAW: Disassembly of section .text: +# RAW: glob: +# RAW-NEXT: {{[0x0-9a-f]+}}: 18 80 84 8f lw $4, -32744($gp) +# RAW-NEXT: {{[0x0-9a-f]+}}: 00 20 84 24 addiu $4, $4, 8192 +# RAW-NEXT: {{[0x0-9a-f]+}}: 1c 80 84 8f lw $4, -32740($gp) +# RAW-NEXT: {{[0x0-9a-f]+}}: 00 22 84 24 addiu $4, $4, 8704 +# RAW-NEXT: {{[0x0-9a-f]+}}: 20 80 84 8f lw $4, -32736($gp) +# RAW-NEXT: {{[0x0-9a-f]+}}: 24 80 84 8f lw $4, -32732($gp) +# RAW-NEXT: {{[0x0-9a-f]+}}: 28 80 84 8f lw $4, -32728($gp) + +# RAW: SYMBOL TABLE: +# RAW: {{[0x0-9a-f]+}} *UND* 00000000 +# RAW: {{[0x0-9a-f]+}} l .data 00000000 str1 +# RAW: {{[0x0-9a-f]+}} l .data 00000005 str2 +# RAW: {{[0x0-9a-f]+}} g F .text 0000001c glob +# RAW: {{[0x0-9a-f]+}} g .data 00000004 hidden + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_ABI_O32, EF_MIPS_ARCH_32 ] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Content: '0000848F000084240100848F000284240000848F0000848F0000848F' + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: .data + Type: R_MIPS_GOT16 + - Offset: 0x04 + Symbol: .data + Type: R_MIPS_LO16 + - Offset: 0x08 + Symbol: .data + Type: R_MIPS_GOT16 + - Offset: 0x0C + Symbol: .data + Type: R_MIPS_LO16 + - Offset: 0x10 + Symbol: hidden + Type: R_MIPS_GOT16 + - Offset: 0x14 + Symbol: glob + Type: R_MIPS_CALL16 + - Offset: 0x18 + Symbol: extern + Type: R_MIPS_CALL16 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x04 + Size: 0x10209 + +Symbols: + Local: + - Name: str1 + Type: STT_OBJECT + Section: .data + Size: 0x10200 + - Name: str2 + Type: STT_OBJECT + Section: .data + Value: 0x10200 + Size: 0x05 + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + Global: + - Name: glob + Section: .text + - Name: hidden + Type: STT_OBJECT + Section: .data + Value: 0x10205 + Size: 0x04 + Visibility: STV_HIDDEN + - Name: extern diff --git a/test/elf/Mips/gotsym.test b/test/elf/Mips/gotsym.test new file mode 100644 index 0000000000000..4581901958aae --- /dev/null +++ b/test/elf/Mips/gotsym.test @@ -0,0 +1,43 @@ +# Check _gp_disp and GOT_OFFSET_TABLE value +# +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mipsel -shared --noinhibit-exec -o %t.so %t.o +# RUN: llvm-objdump -h -t %t.so | FileCheck -check-prefix=SHARED %s + +# SHARED: Sections: +# SHARED: Idx Name Size Address Type +# SHARED: 6 .got 00000008 0000000000001000 DATA +# SHARED: SYMBOL TABLE: +# SHARED: 00001000 g *ABS* 00000000 _GLOBAL_OFFSET_TABLE_ +# SHARED: 00008ff0 g *ABS* 00000000 _gp +# SHARED: 00008ff0 g *ABS* 00000000 _gp_disp + +# RUN: lld -flavor gnu -target mipsel -e main --noinhibit-exec -o %t.exe %t.o +# RUN: llvm-objdump -h -t %t.exe | FileCheck -check-prefix=EXE %s + +# EXE: Sections: +# EXE: Idx Name Size Address Type +# EXE: 7 .got 00000008 0000000000401000 DATA +# EXE: SYMBOL TABLE: +# EXE: 00401000 g *ABS* 00000000 _GLOBAL_OFFSET_TABLE_ +# EXE: 00408ff0 g *ABS* 00000000 _gp +# EXE: 00408ff0 g *ABS* 00000000 _gp_disp + +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32 ] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x04 + +Symbols: + Global: + - Name: main + Section: .text diff --git a/test/elf/Mips/gp-sym-1-micro.test b/test/elf/Mips/gp-sym-1-micro.test new file mode 100644 index 0000000000000..76274eaaa8ac9 --- /dev/null +++ b/test/elf/Mips/gp-sym-1-micro.test @@ -0,0 +1,88 @@ +# Check that microMIPS relocations against __gnu_local_gp +# use "gp" value as target. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o.o %t.so +# RUN: llvm-readobj -symbols %t.exe | FileCheck -check-prefix=SYM %s +# RUN: llvm-objdump -s %t.exe | FileCheck -check-prefix=SEC %s + +# SYM: Name: _gp (203) +# SYM-NEXT: Value: 0x408FF0 + +# SEC: Contents of section .text: +# SEC-NEXT: 400184 00004100 0000f08f 2000bc00 ..A..... ... +# SEC: Contents of section .got: +# SEC-NEXT: 401000 00000000 00000080 ........ + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 12 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + Info: .text + AddressAlign: 0x04 + Relocations: + - Offset: 0x00 + Symbol: __gnu_local_gp + Type: R_MICROMIPS_HI16 + - Offset: 0x04 + Symbol: __gnu_local_gp + Type: R_MICROMIPS_LO16 + - Offset: 0x08 + Symbol: T1 + Type: R_MICROMIPS_26_S1 + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + Global: + - Name: T0 + Type: STT_FUNC + Section: .text + Size: 0x08 + Other: [ STO_MIPS_MICROMIPS ] + - Name: __gnu_local_gp + - Name: T1 +... diff --git a/test/elf/Mips/gp-sym-1.test b/test/elf/Mips/gp-sym-1.test new file mode 100644 index 0000000000000..6c2ffb62629d6 --- /dev/null +++ b/test/elf/Mips/gp-sym-1.test @@ -0,0 +1,86 @@ +# Check that relocations against __gnu_local_gp use "gp" value as target. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o.o %t.so +# RUN: llvm-readobj -symbols %t.exe | FileCheck -check-prefix=SYM %s +# RUN: llvm-objdump -s %t.exe | FileCheck -check-prefix=SEC %s + +# SYM: Name: _gp (203) +# SYM-NEXT: Value: 0x408FF0 + +# SEC: Contents of section .text: +# SEC-NEXT: 400190 41000000 f08f0000 60001000 +# SEC: Contents of section .got: +# SEC-NEXT: 401000 00000000 00000080 + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 12 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + Info: .text + AddressAlign: 0x04 + Relocations: + - Offset: 0x00 + Symbol: __gnu_local_gp + Type: R_MIPS_HI16 + - Offset: 0x04 + Symbol: __gnu_local_gp + Type: R_MIPS_LO16 + - Offset: 0x08 + Symbol: T1 + Type: R_MIPS_26 + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + Global: + - Name: T0 + Type: STT_FUNC + Section: .text + Size: 0x08 + - Name: __gnu_local_gp + - Name: T1 +... diff --git a/test/elf/Mips/gp-sym-2.test b/test/elf/Mips/gp-sym-2.test new file mode 100644 index 0000000000000..6b9e5a5e52d93 --- /dev/null +++ b/test/elf/Mips/gp-sym-2.test @@ -0,0 +1,103 @@ +# Check that R_MIPS32 relocation against __gnu_local_gp causes emitting +# of R_MIPS_REL32 relocation in case of shared library file linking +# and does not produce any dynamic relocation in case of linking a non-shared +# executable file. + +# Now the test failed because the __gnu_local_gp symbol becomes defined +# absolute symbol and we do not generate R_MIPS_REL32 in case of shared +# library linking. +# XFAIL: * + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t-1.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o.o %t-1.so +# RUN: llvm-readobj -r %t.exe | FileCheck -check-prefix=EXE %s +# RUN: lld -flavor gnu -target mipsel -shared -o %t-2.so %t-o.o %t-1.so +# RUN: llvm-readobj -r %t-2.so | FileCheck -check-prefix=SO %s + +# EXE: Relocations [ +# EXE-NEXT: ] + +# SO: Relocations [ +# SO-NEXT: Section (5) .rel.dyn { +# SO-NEXT: 0x0 R_MIPS_NONE - 0x0 +# SO-NEXT: 0x2EC R_MIPS_REL32 __gnu_local_gp 0x0 +# SO-NEXT: } +# SO-NEXT: ] + +# so.so +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + - Name: T2 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_CPIC, EF_MIPS_PIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 12 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + Info: .text + AddressAlign: 0x04 + Relocations: + - Offset: 0x00 + Symbol: __gnu_local_gp + Type: R_MIPS_32 + - Offset: 0x04 + Symbol: T1 + Type: R_MIPS_LO16 + - Offset: 0x08 + Symbol: T2 + Type: R_MIPS_CALL16 + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + Global: + - Name: T0 + Type: STT_FUNC + Section: .text + Size: 0x08 + - Name: __gnu_local_gp + - Name: T1 + - Name: T2 +... diff --git a/test/elf/Mips/hilo16-1.test b/test/elf/Mips/hilo16-1.test new file mode 100644 index 0000000000000..c2863408c888d --- /dev/null +++ b/test/elf/Mips/hilo16-1.test @@ -0,0 +1,44 @@ +# REQUIRES: mips + +# Check handling multiple HI16 relocation followed by a single LO16 relocation. +# +# RUN: llvm-mc -triple=mipsel -filetype=obj -o=%t-obj %s +# RUN: lld -flavor gnu -target mipsel -e glob1 -o %t-exe %t-obj +# RUN: llvm-objdump -t -disassemble %t-exe | FileCheck %s + +# CHECK: Disassembly of section .text: +# CHECK: glob1: +# CHECK-NEXT: {{[0-9a-f]+}}: 40 00 08 3c lui $8, 64 +# CHECK-NEXT: {{[0-9a-f]+}}: 40 00 08 3c lui $8, 64 +# CHECK-NEXT: {{[0-9a-f]+}}: 40 00 08 3c lui $8, 64 +# CHECK-NEXT: {{[0-9a-f]+}}: 40 00 08 3c lui $8, 64 +# CHECK-NEXT: {{[0-9a-f]+}}: 42 00 08 3c lui $8, 66 +# CHECK-NEXT: {{[0-9a-f]+}}: 3e 00 08 3c lui $8, 62 +# CHECK-NEXT: {{[0-9a-f]+}}: 40 02 08 3c lui $8, 576 +# CHECK-NEXT: {{[0-9a-f]+}}: 40 fe 08 3c lui $8, 65088 +# CHECK-NEXT: {{[0-9a-f]+}}: 55 01 08 85 lh $8, 341($8) + +# CHECK: SYMBOL TABLE: +# CHECK: {{[0-9a-f]+}} g F .text 00000024 glob1 +# CHECK: {{[0-9a-f]+}} g F .text 00000004 glob2 + + .global glob1 + .ent glob1 +glob1: + lui $t0,%hi(glob2+0x1) + lui $t0,%hi(glob2+(-0x1)) + lui $t0,%hi(glob2+0x1ff) + lui $t0,%hi(glob2+(-0x1ff)) + lui $t0,%hi(glob2+0x1ffff) + lui $t0,%hi(glob2+(-0x1ffff)) + lui $t0,%hi(glob2+0x1ffffff) + lui $t0,%hi(glob2+(-0x1ffffff)) + + lh $t0,%lo(glob2+(-0x1ffffff))($t0) + .end glob1 + + .global glob2 + .ent glob2 +glob2: + nop + .end glob2 diff --git a/test/elf/Mips/hilo16-2.test b/test/elf/Mips/hilo16-2.test new file mode 100644 index 0000000000000..68cb26eec7881 --- /dev/null +++ b/test/elf/Mips/hilo16-2.test @@ -0,0 +1,68 @@ +# REQUIRES: mips + +# Check handling of HI16 and LO16 relocations for regular symbol. +# +# R_MIPS_HI16: (AHL + S) - (short)(AHL + S) +# R_MIPS_LO16: AHL + S +# where AHL = (AHI << 16) + ALO +# +# RUN: llvm-mc -triple=mipsel -filetype=obj -o=%t-obj %s +# RUN: lld -flavor gnu -target mipsel -e glob1 -o %t-exe %t-obj +# RUN: llvm-objdump -t -disassemble %t-exe | FileCheck %s + +# CHECK: Disassembly of section .text: +# CHECK: glob1: +# CHECK-NEXT: {{[0-9a-f]+}}: 40 00 08 3c lui $8, 64 +# CHECK-NEXT: {{[0-9a-f]+}}: 71 01 08 85 lh $8, 369($8) +# CHECK-NEXT: {{[0-9a-f]+}}: 40 00 08 3c lui $8, 64 +# CHECK-NEXT: {{[0-9a-f]+}}: 6f 01 08 85 lh $8, 367($8) +# CHECK-NEXT: {{[0-9a-f]+}}: 40 00 08 3c lui $8, 64 +# CHECK-NEXT: {{[0-9a-f]+}}: 6f 03 08 85 lh $8, 879($8) +# CHECK-NEXT: {{[0-9a-f]+}}: 40 00 08 3c lui $8, 64 +# CHECK-NEXT: {{[0-9a-f]+}}: 71 ff 08 85 lh $8, -143($8) +# CHECK-NEXT: {{[0-9a-f]+}}: 42 00 08 3c lui $8, 66 +# CHECK-NEXT: {{[0-9a-f]+}}: 6f 01 08 85 lh $8, 367($8) +# CHECK-NEXT: {{[0-9a-f]+}}: 3e 00 08 3c lui $8, 62 +# CHECK-NEXT: {{[0-9a-f]+}}: 71 01 08 85 lh $8, 369($8) +# CHECK-NEXT: {{[0-9a-f]+}}: 40 02 08 3c lui $8, 576 +# CHECK-NEXT: {{[0-9a-f]+}}: 6f 01 08 85 lh $8, 367($8) +# CHECK-NEXT: {{[0-9a-f]+}}: 40 fe 08 3c lui $8, 65088 +# CHECK-NEXT: {{[0-9a-f]+}}: 71 01 08 85 lh $8, 369($8) + +# CHECK: SYMBOL TABLE: +# CHECK: {{[0-9a-f]+}} g F .text 00000040 glob1 +# CHECK: {{[0-9a-f]+}} g F .text 00000004 glob2 + + .global glob1 + .ent glob1 +glob1: + lui $t0,%hi(glob2+0x1) + lh $t0,%lo(glob2+0x1)($t0) + + lui $t0,%hi(glob2+(-0x1)) + lh $t0,%lo(glob2+(-0x1))($t0) + + lui $t0,%hi(glob2+0x1ff) + lh $t0,%lo(glob2+0x1ff)($t0) + + lui $t0,%hi(glob2+(-0x1ff)) + lh $t0,%lo(glob2+(-0x1ff))($t0) + + lui $t0,%hi(glob2+0x1ffff) + lh $t0,%lo(glob2+0x1ffff)($t0) + + lui $t0,%hi(glob2+(-0x1ffff)) + lh $t0,%lo(glob2+(-0x1ffff))($t0) + + lui $t0,%hi(glob2+0x1ffffff) # truncate + lh $t0,%lo(glob2+0x1ffffff)($t0) + + lui $t0,%hi(glob2+(-0x1ffffff)) # truncate + lh $t0,%lo(glob2+(-0x1ffffff))($t0) + .end glob1 + + .global glob2 + .ent glob2 +glob2: + nop + .end glob2 diff --git a/test/elf/Mips/hilo16-3.test b/test/elf/Mips/hilo16-3.test new file mode 100644 index 0000000000000..daf4807d719a9 --- /dev/null +++ b/test/elf/Mips/hilo16-3.test @@ -0,0 +1,45 @@ +# REQUIRES: mips + +# Check handling of HI16 and LO16 relocations for _gp_disp. +# +# R_MIPS_HI16: (AHL + GP - P) - (short)(AHL + GP - P) +# R_MIPS_LO16: AHL + GP - P + 4 +# where AHL = (AHI << 16) + ALO +# +# RUN: llvm-mc -triple=mipsel -filetype=obj -o=%t-obj %s +# RUN: lld -flavor gnu -target mipsel -shared -o %t-so %t-obj +# RUN: llvm-objdump -t -disassemble %t-so | FileCheck %s + +# CHECK: Disassembly of section .text: +# CHECK: glob1: +# CHECK-NEXT: {{[0-9a-f]+}}: 01 00 08 3c lui $8, 1 +# CHECK-NEXT: {{[0-9a-f]+}}: 01 8f 08 85 lh $8, -28927($8) +# CHECK-NEXT: {{[0-9a-f]+}}: 01 00 08 3c lui $8, 1 +# CHECK-NEXT: {{[0-9a-f]+}}: f7 8e 08 85 lh $8, -28937($8) +# CHECK-NEXT: {{[0-9a-f]+}}: 01 00 08 3c lui $8, 1 +# CHECK-NEXT: {{[0-9a-f]+}}: ef 90 08 85 lh $8, -28433($8) +# CHECK-NEXT: {{[0-9a-f]+}}: 01 00 08 3c lui $8, 1 +# CHECK-NEXT: {{[0-9a-f]+}}: e9 8c 08 85 lh $8, -29463($8) +# CHECK-NEXT: {{[0-9a-f]+}}: 03 00 08 3c lui $8, 3 +# CHECK-NEXT: {{[0-9a-f]+}}: df 8e 08 85 lh $8, -28961($8) + +# CHECK: SYMBOL TABLE: +# CHECK: {{[0-9a-f]+}} g F .text 00000028 glob1 +# CHECK: {{[0-9a-f]+}} g *ABS* 00000000 _gp_disp + + .global glob1 +glob1: + lui $t0,%hi(_gp_disp+0x1) + lh $t0,%lo(_gp_disp+0x1)($t0) + + lui $t0,%hi(_gp_disp+(-0x1)) + lh $t0,%lo(_gp_disp+(-0x1))($t0) + + lui $t0,%hi(_gp_disp+0x1ff) + lh $t0,%lo(_gp_disp+0x1ff)($t0) + + lui $t0,%hi(_gp_disp+(-0x1ff)) + lh $t0,%lo(_gp_disp+(-0x1ff))($t0) + + lui $t0,%hi(_gp_disp+0x1ffff) + lh $t0,%lo(_gp_disp+0x1ffff)($t0) diff --git a/test/elf/Mips/hilo16-4.test b/test/elf/Mips/hilo16-4.test new file mode 100644 index 0000000000000..8a13f7b131fd3 --- /dev/null +++ b/test/elf/Mips/hilo16-4.test @@ -0,0 +1,93 @@ +# REQUIRES: mips + +# Check pairing of R_MIPS_HI16 and R_MIPS_LO16 relocations. +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: lld -flavor gnu -target mipsel -e glob1 -o %t-exe %t-obj +# RUN: llvm-objdump -t -disassemble %t-exe | FileCheck %s + +# CHECK: Disassembly of section .text: +# CHECK: glob1: +# CHECK-NEXT: 400130: 40 00 04 3c lui $4, 64 +# CHECK-NEXT: 400134: ff 9f a6 8c lw $6, -24577($5) + +# CHECK: glob2: +# CHECK-NEXT: 400138: 00 20 c7 80 lb $7, 8192($6) +# CHECK-NEXT: 40013c: 04 20 c8 80 lb $8, 8196($6) + +# CHECK: glob3: +# CHECK-NEXT: 400140: 40 80 05 3c lui $5, 32832 + +# CHECK: SYMBOL TABLE: +# CHECK: 00400130 g F .text 00000008 glob1 +# CHECK: 00400138 g F .text 00000008 glob2 +# CHECK: 00400140 g F .text 00000004 glob3 +# CHECK: 00402000 g .data 0000000c X + +!ELF +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS +# glob1: +# lui $4,%hi(X) # rel A +# lw $6,%lo(X+32767)($5) # rel B +# glob2: +# lb $7,%lo(X)($6) # rel C +# lb $8,%lo(X+4)($6) # rel D +# glob3: +# lui $5,%hi(X+32767) # rel E + Content: "0000043CFF7FA68C0000C7800400C880FF7F053C" + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Content: "000000000000000000000000" + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x10 # rel E + Symbol: X + Type: R_MIPS_HI16 + - Offset: 0x04 # rel B + Symbol: X + Type: R_MIPS_LO16 + - Offset: 0x00 # rel A + Symbol: X + Type: R_MIPS_HI16 + - Offset: 0x0C # rel D + Symbol: X + Type: R_MIPS_LO16 + - Offset: 0x08 # rel C + Symbol: X + Type: R_MIPS_LO16 + +Symbols: + Global: + - Name: glob1 + Section: .text + Value: 0x0 + Size: 8 + - Name: glob2 + Section: .text + Value: 0x8 + Size: 8 + - Name: glob3 + Section: .text + Value: 0x10 + Size: 4 + - Name: X + Section: .data + Value: 0x0 + Size: 12 diff --git a/test/elf/Mips/hilo16-5.test b/test/elf/Mips/hilo16-5.test new file mode 100644 index 0000000000000..91aca8b1c3662 --- /dev/null +++ b/test/elf/Mips/hilo16-5.test @@ -0,0 +1,103 @@ +# Check that linker shows a warning when +# there is orphaned R_MIPS_HI16 relocation. + +# RUN: yaml2obj -format=elf -o %t-so.o -docnum 1 %s +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o +# RUN: yaml2obj -format=elf -o %t-o.o -docnum 2 %s +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o.o %t.so 2>&1 \ +# RUN: | FileCheck -check-prefix=DIAG %s +# RUN: llvm-objdump -s %t.exe | FileCheck -check-prefix=DATA %s + +# DIAG: lld warning: cannot matching LO16 relocation +# DIAG: lld warning: cannot matching LO16 relocation + +# DATA: Contents of section .data: +# DATA-NEXT: 402000 40000000 10200000 40000000 @.... ..@... + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .data + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +Symbols: + Global: + - Name: D1 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 4 + - Name: D2 + Section: .data + Type: STT_OBJECT + Value: 0x4 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "00000000" + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Content: "000000000000000000000000" + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: D1 + Type: R_MIPS_HI16 + - Offset: 0x08 + Symbol: D2 + Type: R_MIPS_HI16 + - Offset: 0x04 + Symbol: D1 + Type: R_MIPS_LO16 + - Offset: 0x08 + Symbol: .text + Type: R_MIPS_HI16 + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + - Name: D1 + - Name: D2 +... diff --git a/test/elf/Mips/hilo16-8-micro.test b/test/elf/Mips/hilo16-8-micro.test new file mode 100644 index 0000000000000..3248804f8f540 --- /dev/null +++ b/test/elf/Mips/hilo16-8-micro.test @@ -0,0 +1,81 @@ +# REQUIRES: mips + +# Check calculation of AHL addendums for R_MICROMIPS_HI16 / R_MICROMIPS_LO16 +# relocations for a regular symbol. +# +# RUN: llvm-mc -triple=mipsel -mattr=micromips -filetype=obj -o=%t-obj %s +# RUN: lld -flavor gnu -target mipsel -e glob1 -o %t-exe %t-obj +# RUN: llvm-objdump -t -d -mattr=micromips %t-exe | FileCheck %s + +# CHECK: Disassembly of section .text: +# CHECK-NEXT: glob1: +# CHECK-NEXT: 400130: a8 41 40 00 lui $8, 64 +# CHECK-NEXT: 400134: 08 3d 6a 01 lh $8, 362($8) +# CHECK-NEXT: 400138: a8 41 41 00 lui $8, 65 +# CHECK-NEXT: 40013c: 08 3d 68 81 lh $8, -32408($8) +# CHECK-NEXT: 400140: a8 41 41 00 lui $8, 65 +# CHECK-NEXT: 400144: 08 3d e9 81 lh $8, -32279($8) +# CHECK-NEXT: 400148: a8 41 42 00 lui $8, 66 +# CHECK-NEXT: 40014c: 08 3d 69 81 lh $8, -32407($8) +# CHECK-NEXT: 400150: a8 41 40 40 lui $8, 16448 +# CHECK-NEXT: 400154: 08 3d 69 01 lh $8, 361($8) +# CHECK-NEXT: 400158: a8 41 40 80 lui $8, 32832 +# CHECK-NEXT: 40015c: 08 3d 69 01 lh $8, 361($8) +# CHECK-NEXT: 400160: a8 41 c1 80 lui $8, 32961 +# CHECK-NEXT: 400164: 08 3d e9 81 lh $8, -32279($8) + +# CHECK: glob2: +# CHECK-NEXT: 400168: a8 41 40 00 lui $8, 64 +# CHECK-NEXT: 40016c: a8 41 40 00 lui $8, 64 +# CHECK-NEXT: 400170: a8 41 41 00 lui $8, 65 +# CHECK-NEXT: 400174: a8 41 42 00 lui $8, 66 +# CHECK-NEXT: 400178: a8 41 40 40 lui $8, 16448 +# CHECK-NEXT: 40017c: a8 41 40 80 lui $8, 32832 +# CHECK-NEXT: 400180: a8 41 c1 80 lui $8, 32961 +# CHECK-NEXT: 400184: 08 3d b1 81 lh $8, -32335($8) + +# CHECK: SYMBOL TABLE: +# CHECK: 00400130 g F .text 00000038 glob1 +# CHECK: 00400168 g F .text 00000020 glob2 + + .globl glob1 + .type glob1, @function + .set micromips + .ent glob1 +glob1: + lui $t0,%hi(glob2+0x00000001) + lh $t0,%lo(glob2+0x00000001)($t0) + + lui $t0,%hi(glob2+0x00007fff) + lh $t0,%lo(glob2+0x00007fff)($t0) + + lui $t0,%hi(glob2+0x00008080) + lh $t0,%lo(glob2+0x00008080)($t0) + + lui $t0,%hi(glob2+0x00018000) + lh $t0,%lo(glob2+0x00018000)($t0) + + lui $t0,%hi(glob2+0x40000000) + lh $t0,%lo(glob2+0x40000000)($t0) + + lui $t0,%hi(glob2+0x80000000) + lh $t0,%lo(glob2+0x80000000)($t0) + + lui $t0,%hi(glob2+0x80808080) + lh $t0,%lo(glob2+0x80808080)($t0) + .end glob1 + + .globl glob2 + .type glob2, @function + .set micromips + .ent glob2 +glob2: + lui $t0,%hi(glob1+0x00000001) + lui $t0,%hi(glob1+0x00007fff) + lui $t0,%hi(glob1+0x00008080) + lui $t0,%hi(glob1+0x00018000) + lui $t0,%hi(glob1+0x40000000) + lui $t0,%hi(glob1+0x80000000) + lui $t0,%hi(glob1+0x80808080) + lh $t0,%lo(glob1+0x80808080)($t0) + .end glob2 diff --git a/test/elf/Mips/hilo16-9-micro.test b/test/elf/Mips/hilo16-9-micro.test new file mode 100644 index 0000000000000..30ad10ce0dd4a --- /dev/null +++ b/test/elf/Mips/hilo16-9-micro.test @@ -0,0 +1,68 @@ +# REQUIRES: mips + +# Check calculation of AHL addendums for R_MICROMIPS_HI16 / R_MICROMIPS_LO16 +# relocations for the _gp_disp symbol. +# +# RUN: llvm-mc -triple=mipsel -mattr=micromips -filetype=obj -o=%t-obj %s +# RUN: lld -flavor gnu -target mipsel -shared -o %t-so %t-obj +# RUN: llvm-objdump -t -d -mattr=micromips %t-so | FileCheck %s + +# CHECK: Disassembly of section .text: +# CHECK-NEXT: glob1: +# CHECK-NEXT: 130: a8 41 01 00 lui $8, 1 +# CHECK-NEXT: 134: 08 3d c0 9e lh $8, -24896($8) +# CHECK-NEXT: 138: a8 41 01 00 lui $8, 1 +# CHECK-NEXT: 13c: 08 3d b6 1e lh $8, 7862($8) +# CHECK-NEXT: 140: a8 41 01 00 lui $8, 1 +# CHECK-NEXT: 144: 08 3d 2f 1f lh $8, 7983($8) +# CHECK-NEXT: 148: a8 41 02 00 lui $8, 2 +# CHECK-NEXT: 14c: 08 3d a7 1e lh $8, 7847($8) +# CHECK-NEXT: 150: a8 41 01 40 lui $8, 16385 +# CHECK-NEXT: 154: 08 3d 9f 9e lh $8, -24929($8) + +# CHECK: glob2: +# CHECK-NEXT: 158: a8 41 01 00 lui $8, 1 +# CHECK-NEXT: 15c: a8 41 01 00 lui $8, 1 +# CHECK-NEXT: 160: a8 41 02 00 lui $8, 2 +# CHECK-NEXT: 164: a8 41 03 00 lui $8, 3 +# CHECK-NEXT: 168: a8 41 01 40 lui $8, 16385 +# CHECK-NEXT: 16c: 08 3d 87 9e lh $8, -24953($8) + +# CHECK: SYMBOL TABLE: +# CHECK: 00000130 g F .text 00000028 glob1 +# CHECK: 00000158 g F .text 00000018 glob2 +# CHECK: 00009ff0 g *ABS* 00000000 _gp_disp + + .globl glob1 + .type glob1, @function + .set micromips + .ent glob1 +glob1: + lui $t0,%hi(_gp_disp+0x00000001) + lh $t0,%lo(_gp_disp+0x00000001)($t0) + + lui $t0,%hi(_gp_disp+0x00007fff) + lh $t0,%lo(_gp_disp+0x00007fff)($t0) + + lui $t0,%hi(_gp_disp+0x00008080) + lh $t0,%lo(_gp_disp+0x00008080)($t0) + + lui $t0,%hi(_gp_disp+0x00018000) + lh $t0,%lo(_gp_disp+0x00018000)($t0) + + lui $t0,%hi(_gp_disp+0x40000000) + lh $t0,%lo(_gp_disp+0x40000000)($t0) + .end glob1 + + .globl glob2 + .type glob2, @function + .set micromips + .ent glob2 +glob2: + lui $t0,%hi(_gp_disp+0x00000001) + lui $t0,%hi(_gp_disp+0x00007fff) + lui $t0,%hi(_gp_disp+0x00008080) + lui $t0,%hi(_gp_disp+0x00018000) + lui $t0,%hi(_gp_disp+0x40000000) + lh $t0,%lo(_gp_disp+0x40000000)($t0) + .end glob2 diff --git a/test/elf/Mips/initfini-micro.test b/test/elf/Mips/initfini-micro.test new file mode 100644 index 0000000000000..ba30e89ade215 --- /dev/null +++ b/test/elf/Mips/initfini-micro.test @@ -0,0 +1,45 @@ +# Check that if _init/_fini symbols are microMIPS encoded, DT_INIT/DT_FINI tags +# use adjusted values with set the last bit. + +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t.o +# RUN: llvm-readobj -symbols -dynamic-table %t.so | FileCheck %s + +# CHECK: Name: _init (1) +# CHECK-NEXT: Value: 0xF5 +# CHECK: Name: _fini (7) +# CHECK-NEXT: Value: 0xF9 +# +# CHECK: 0x0000000C INIT 0xF5 +# CHECK: 0x0000000D FINI 0xF9 + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x18 + +Symbols: + Global: + - Name: _init + Type: STT_FUNC + Section: .text + Value: 0x0 + Size: 0x4 + Other: [ STO_MIPS_MICROMIPS ] + - Name: _fini + Type: STT_FUNC + Section: .text + Value: 0x4 + Size: 0x4 + Other: [ STO_MIPS_MICROMIPS ] +... diff --git a/test/elf/Mips/interpreter-64.test b/test/elf/Mips/interpreter-64.test new file mode 100644 index 0000000000000..3ece3e6a467a8 --- /dev/null +++ b/test/elf/Mips/interpreter-64.test @@ -0,0 +1,26 @@ +# Check program interpreter setup. +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mips64el -e main -o %t.exe %t.o +# RUN: llvm-objdump -s %t.exe | FileCheck %s + +# CHECK: Contents of section .interp: +# CHECK-NEXT: {{[0-9a-f]+}} 2f6c6962 36342f6c 642e736f 2e3100 /lib64/ld.so.1. + +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ARCH_64 ] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x08 + +Symbols: + Global: + - Name: main + Section: .text diff --git a/test/elf/Mips/interpreter.test b/test/elf/Mips/interpreter.test new file mode 100644 index 0000000000000..5355f7709fbdf --- /dev/null +++ b/test/elf/Mips/interpreter.test @@ -0,0 +1,26 @@ +# Check program interpreter setup. +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mipsel -e main -o %t.exe %t.o +# RUN: llvm-objdump -s %t.exe | FileCheck %s + +# CHECK: Contents of section .interp: +# CHECK-NEXT: {{[0-9a-f]+}} 2f6c6962 2f6c642e 736f2e31 00 /lib/ld.so.1. + +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32 ] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x04 + +Symbols: + Global: + - Name: main + Section: .text diff --git a/test/elf/Mips/invalid-reginfo.test b/test/elf/Mips/invalid-reginfo.test new file mode 100644 index 0000000000000..d56223bf2e046 --- /dev/null +++ b/test/elf/Mips/invalid-reginfo.test @@ -0,0 +1,28 @@ +# Check that LLD shows an error if .reginfo section has invalid size + +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: not lld -flavor gnu -target mipsel -o %t.exe %t.o 2>&1 | FileCheck %s + +# CHECK: Invalid size of MIPS_REGINFO section + +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_ABI_O32, EF_MIPS_ARCH_32 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + - Name: .reginfo + Type: SHT_MIPS_REGINFO + Flags: [ SHF_ALLOC ] + AddressAlign: 0x01 + Size: 0x25 +Symbols: + Global: + - Name: main + Section: .text diff --git a/test/elf/Mips/jalx-align-err.test b/test/elf/Mips/jalx-align-err.test new file mode 100644 index 0000000000000..3db18fc98fac7 --- /dev/null +++ b/test/elf/Mips/jalx-align-err.test @@ -0,0 +1,46 @@ +# Check that LLD shows an error if jalx target value is not word-aligned. + +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: not lld -flavor gnu -target mipsel -e T0 -o %t-exe %t-obj 2>&1 \ +# RUN: | FileCheck %s + +# CHECK: The jalx target 0x400116 is not word-aligned + +!ELF +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, + EF_MIPS_MICROMIPS ] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 8 + AddressAlign: 16 + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0 + Symbol: T1 + Type: R_MICROMIPS_26_S1 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0 + Size: 4 + Other: [ STO_MIPS_MICROMIPS ] + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 6 + Size: 2 diff --git a/test/elf/Mips/jump-fix-err.test b/test/elf/Mips/jump-fix-err.test new file mode 100644 index 0000000000000..9811799383978 --- /dev/null +++ b/test/elf/Mips/jump-fix-err.test @@ -0,0 +1,45 @@ +# Check that LLD shows an error in case +# of replacing an unknown unstruction by jalx. + +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: not lld -flavor gnu -target mipsel -o %t-exe %t-obj 2>&1 | FileCheck %s + +# CHECK: Unsupported jump opcode (0x0) for ISA modes cross call + +!ELF +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, + EF_MIPS_MICROMIPS ] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 8 + AddressAlign: 16 + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0 + Symbol: T0 + Type: R_MICROMIPS_26_S1 + +Symbols: + Global: + - Name: __start + Section: .text + Type: STT_FUNC + Size: 4 + Other: [ STO_MIPS_MICROMIPS ] + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 4 + Size: 4 diff --git a/test/elf/Mips/la25-stub-micro.test b/test/elf/Mips/la25-stub-micro.test new file mode 100644 index 0000000000000..d41297a0ddc3c --- /dev/null +++ b/test/elf/Mips/la25-stub-micro.test @@ -0,0 +1,140 @@ +# Check microMIPS LA25 stubs creation when PIC code +# is called from non-PIC routines. + +# Build executable from pic and non-pic code. +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-npic.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-pic.o +# RUN: yaml2obj -format=elf -docnum 3 %s > %t-main.o +# RUN: lld -flavor gnu -target mipsel -e glob -o %t.exe \ +# RUN: %t-npic.o %t-pic.o %t-main.o + +# RUN: llvm-readobj -t %t.exe | FileCheck -check-prefix=SYM %s +# RUN: llvm-objdump -s %t.exe | FileCheck -check-prefix=ASM %s + +# SYM: Name: loc (13) +# SYM-NEXT: Value: 0x400135 +# SYM: Name: T1N (1) +# SYM-NEXT: Value: 0x400111 +# SYM: Name: T1 (5) +# SYM-NEXT: Value: 0x400121 +# SYM: Name: glob (8) +# SYM-NEXT: Value: 0x400125 + +# ASM: Contents of section .text: +# ASM-NEXT: 400110 00000000 00000000 00000000 00000000 +# ASM-NEXT: 400120 00000000 00000000 00000000 10f04900 +# 0x100049 << 2 == 0x400125 (jalx glob) --^ +# ASM-NEXT: 400130 00000000 20f49200 00000000 20f48800 +# ^-- 0x100049 << 2 == 0x400124 (jal glob) +# 0x100044 << 2 == 0x400110 (jal T1N) --^ +# ASM-NEXT: 400140 00000000 20f4a800 00000000 00000000 +# ^-- 0x100054 << 2 == 0x400150 (jal T1 stub) +# ASM-NEXT: 400150 b9414000 20d49000 39332101 00000000 +# ^-- j 0x400120 (T1) + +# npic.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, + EF_MIPS_CPIC, EF_MIPS_MICROMIPS ] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x04 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1N + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + Other: [ STO_MIPS_MICROMIPS ] + +# pic.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, + EF_MIPS_CPIC, EF_MIPS_PIC, EF_MIPS_MICROMIPS ] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x04 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + Other: [ STO_MIPS_MICROMIPS ] + +# main.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, + EF_MIPS_CPIC, EF_MIPS_MICROMIPS ] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Content: '000000000000000000f400000000000000f400000000000000f400000000000000f4000000000000' +# jal loc jal glob jal T1N jal T1 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x08 + Symbol: .text + Type: R_MICROMIPS_26_S1 + - Offset: 0x10 + Symbol: glob + Type: R_MICROMIPS_26_S1 + - Offset: 0x18 + Symbol: T1N + Type: R_MICROMIPS_26_S1 + - Offset: 0x20 + Symbol: T1 + Type: R_MICROMIPS_26_S1 + +Symbols: + Local: + - Name: loc + Section: .text + Value: 0x10 + Size: 0x18 + Other: [ STO_MIPS_MICROMIPS ] + - Name: .text + Type: STT_SECTION + Section: .text + Global: + - Name: glob + Section: .text + Size: 0x10 + Other: [ STO_MIPS_MICROMIPS ] + - Name: T1 + - Name: T1N +... diff --git a/test/elf/Mips/la25-stub.test b/test/elf/Mips/la25-stub.test new file mode 100644 index 0000000000000..2c4b26452cecb --- /dev/null +++ b/test/elf/Mips/la25-stub.test @@ -0,0 +1,133 @@ +# Check LA25 stubs creation when PIC code is called from non-PIC routines. + +# Build executable from pic and non-pic code. +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-npic.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-pic.o +# RUN: yaml2obj -format=elf -docnum 3 %s > %t-main.o +# RUN: lld -flavor gnu -target mipsel -e glob -o %t.exe \ +# RUN: %t-npic.o %t-pic.o %t-main.o + +# RUN: llvm-readobj -t %t.exe | FileCheck -check-prefix=SYM %s +# RUN: llvm-objdump -s %t.exe | FileCheck -check-prefix=ASM %s + +# SYM: Name: loc (13) +# SYM-NEXT: Value: 0x400134 +# SYM: Name: T1N (1) +# SYM-NEXT: Value: 0x400110 +# SYM: Name: T1 (5) +# SYM-NEXT: Value: 0x400120 +# SYM: Name: glob (8) +# SYM-NEXT: Value: 0x400124 + +# ASM: Contents of section .text: +# ASM-NEXT: 400110 00000000 00000000 00000000 00000000 +# ASM-NEXT: 400120 00000000 00000000 00000000 49001000 +# 0x100049 << 2 == 0x400124 (glob) --^ +# ASM-NEXT: 400130 00000000 49001000 00000000 44001000 +# ^-- 0x100049 << 2 == 0x400124 (glob) +# 0x100044 << 2 == 0x400110 (T1N) --^ +# ASM-NEXT: 400140 00000000 54001000 00000000 00000000 +# ^-- 0x100054 << 2 == 0x400150 (T1 stub) +# ASM-NEXT: 400150 4000193c 48001008 20013927 00000000 +# ^-- j 0x400120 (T1) + +# npic.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_ABI_O32, EF_MIPS_ARCH_32, EF_MIPS_CPIC ] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x04 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1N + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + +# pic.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_ABI_O32, EF_MIPS_ARCH_32, EF_MIPS_CPIC, EF_MIPS_PIC ] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x04 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + +# main.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_ABI_O32, EF_MIPS_ARCH_32, EF_MIPS_CPIC ] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x28 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x08 + Symbol: .text + Type: R_MIPS_26 + Addend: 0 + - Offset: 0x10 + Symbol: glob + Type: R_MIPS_26 + Addend: 0 + - Offset: 0x18 + Symbol: T1N + Type: R_MIPS_26 + Addend: 0 + - Offset: 0x20 + Symbol: T1 + Type: R_MIPS_26 + Addend: 0 + +Symbols: + Local: + - Name: loc + Section: .text + Value: 0x10 + - Name: .text + Type: STT_SECTION + Section: .text + Global: + - Name: glob + Section: .text + - Name: T1 + - Name: T1N +... diff --git a/test/elf/Mips/mips-options-gp0.test b/test/elf/Mips/mips-options-gp0.test new file mode 100644 index 0000000000000..339ab97253b63 --- /dev/null +++ b/test/elf/Mips/mips-options-gp0.test @@ -0,0 +1,78 @@ +# Check reading GP0 value from .MIPS.options section +# +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mipsel -e G1 -shared -o %t.so %t.o +# RUN: llvm-readobj -symbols %t.so | FileCheck -check-prefix=SYM %s +# RUN: llvm-objdump -s %t.so | FileCheck -check-prefix=SEC %s + +# SYM: Name: L1 (1) +# SYM-NEXT: Value: 0xCC +# SYM-NEXT: Size: 4 +# SYM-NEXT: Binding: Local (0x0) +# SYM-NEXT: Type: Function (0x2) +# SYM-NEXT: Other: 0 +# SYM-NEXT: Section: .text (0x4) + +# SYM: Name: _gp (34) +# SYM-NEXT: Value: 0x8FF0 +# SYM-NEXT: Size: 0 +# SYM-NEXT: Binding: Global (0x1) +# SYM-NEXT: Type: Object (0x1) +# SYM-NEXT: Other: 0 +# SYM-NEXT: Section: Absolute (0xFFF1) + +# 0xffff80dc == 0x0 (addend) + 0x00cc (L1) + 0x1000 (GP0) - 0x8ff0 (_gp) +# SEC: Contents of section .rodata: +# SEC-NEXT: 00d4 dc80ffff 00000000 00000000 00000000 ................ + +!ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ] +Sections: +- Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x08 + +- Name: .rodata + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x04 + Size: 16 + +- Name: .rel.rodata + Type: SHT_REL + Link: .symtab + Info: .rodata + AddressAlign: 0x04 + Relocations: + - Offset: 0 + Symbol: L1 + Type: R_MIPS_GPREL32 + +- Name: .MIPS.options + Type: SHT_MIPS_OPTIONS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x01 + Content: "0128000000000000000000000000000000000000000000000000000000100000" + +Symbols: + Local: + - Name: L1 + Section: .text + Value: 0x00 + Size: 0x04 + - Name: .rodata + Type: STT_SECTION + Section: .rodata + Global: + - Name: G1 + Section: .text + Value: 0x04 + Size: 0x04 diff --git a/test/elf/Mips/n64-rel-chain.test b/test/elf/Mips/n64-rel-chain.test new file mode 100644 index 0000000000000..0ae7af73ae833 --- /dev/null +++ b/test/elf/Mips/n64-rel-chain.test @@ -0,0 +1,134 @@ +# Check handling MIPS N64 ABI relocation "chains". + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mips64el -shared -o %t.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mips64el -e T0 -o %t.exe %t-o.o %t.so +# RUN: llvm-objdump -s %t.exe | FileCheck %s + +# CHECK: Contents of section .text: +# CHECK-NEXT: 1200001d0 01000000 00000000 208e0000 00000000 ........ ....... +# CHECK-NEXT: 1200001e0 20800000 f8010000 28800000 00000000 .......(....... +# CHECK: Contents of section .pdr: +# CHECK-NEXT: 0000 d0010020 e0010020 ... ... + +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ARCH_64] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 16 + Size: 8 + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + + Global: + - Name: T1 + Type: STT_FUNC + Section: .text + Value: 0 + Size: 8 + +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ARCH_64] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 16 + Size: 32 + + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 8 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: LT1 + Type: R_MIPS_GPREL16 + Type2: R_MIPS_SUB + Type3: R_MIPS_HI16 + - Offset: 0x08 + Symbol: LT1 + Type: R_MIPS_GPREL16 + Type2: R_MIPS_SUB + Type3: R_MIPS_LO16 + - Offset: 0x10 + Symbol: .rodata + Type: R_MIPS_GOT_PAGE + Addend: 8 + - Offset: 0x14 + Symbol: .rodata + Type: R_MIPS_GOT_OFST + Addend: 8 + - Offset: 0x18 + Symbol: T1 + Type: R_MIPS_CALL16 + + - Name: .pdr + Type: SHT_PROGBITS + AddressAlign: 4 + Size: 8 + + - Name: .rela.pdr + Type: SHT_RELA + Link: .symtab + AddressAlign: 8 + Info: .pdr + Relocations: + - Offset: 0x00 + Symbol: LT1 + Type: R_MIPS_32 + - Offset: 0x04 + Symbol: T0 + Type: R_MIPS_32 + + - Name: .rodata + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 16 + Size: 16 + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .rodata + Type: STT_SECTION + Section: .rodata + - Name: .pdr + Type: STT_SECTION + Section: .pdr + + Global: + - Name: LT1 + Type: STT_FUNC + Section: .text + Value: 0x00 + Size: 0x10 + - Name: T0 + Type: STT_FUNC + Section: .text + Value: 0x10 + Size: 0x10 + - Name: T1 +... diff --git a/test/elf/Mips/opt-emulation.test b/test/elf/Mips/opt-emulation.test new file mode 100644 index 0000000000000..2d1e7142c3867 --- /dev/null +++ b/test/elf/Mips/opt-emulation.test @@ -0,0 +1,41 @@ +# Check MIPS specific arguments of the -m command line option. + +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: lld -flavor gnu -target mipsel -m elf32ltsmip -o %t-exe %t-obj +# RUN: llvm-readobj -file-headers %t-exe | FileCheck -check-prefix=LE-O32 %s + +# LE-O32: Class: 32-bit (0x1) +# LE-O32: DataEncoding: LittleEndian (0x1) +# LE-O32: FileVersion: 1 +# LE-O32: OS/ABI: SystemV (0x0) +# LE-O32: ABIVersion: 0 +# LE-O32: Machine: EM_MIPS (0x8) +# LE-O32: Version: 1 +# LE-O32: Flags [ (0x70001005) +# LE-O32-NEXT: EF_MIPS_ABI_O32 (0x1000) +# LE-O32-NEXT: EF_MIPS_ARCH_32R2 (0x70000000) +# LE-O32-NEXT: EF_MIPS_CPIC (0x4) +# LE-O32-NEXT: EF_MIPS_NOREORDER (0x1) +# LE-O32-NEXT: ] + +!ELF +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_NOREORDER, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "00000000" + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: __start + Section: .text + Value: 0x0 + Size: 4 diff --git a/test/elf/Mips/pc23-range.test b/test/elf/Mips/pc23-range.test new file mode 100644 index 0000000000000..7166176c46288 --- /dev/null +++ b/test/elf/Mips/pc23-range.test @@ -0,0 +1,56 @@ +# Check that LLD shows an error if ADDIUPC immediate is out of range. + +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: lld -flavor gnu -target mipsel -o %t-exe %t-obj 2>&1 | FileCheck %s + +# CHECK: The addiupc instruction immediate 0x02000008 is out of range + +!ELF +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, + EF_MIPS_MICROMIPS ] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "0000000080780100" +# ^ PC23: 1 << 2 = 4 => T0 + 4 - 4 = T0 + AddressAlign: 16 + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + +- Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x04 + Size: 0x4000000 + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 4 + Symbol: T0 + Type: R_MICROMIPS_PC23_S2 + +Symbols: + Global: + - Name: __start + Section: .text + Type: STT_FUNC + Size: 8 + Other: [ STO_MIPS_MICROMIPS ] + - Name: TZ + Section: .data + Type: STT_FUNC + Value: 0 + Size: 0x2000000 + - Name: T0 + Section: .data + Type: STT_FUNC + Value: 0x2000000 + Size: 4 diff --git a/test/elf/Mips/plt-entry-mixed-1.test b/test/elf/Mips/plt-entry-mixed-1.test new file mode 100644 index 0000000000000..bc45763fa16d9 --- /dev/null +++ b/test/elf/Mips/plt-entry-mixed-1.test @@ -0,0 +1,114 @@ +# REQUIRES: mips + +# Conditions: +# a) Object file contains both R_MIPS_26 and microMIPS non-jal relocations. +# b) The R_MIPS_26 relocation handled first. +# Check: +# a) PLT contains the only regular entry. + +# Build shared library +# RUN: yaml2obj -format=elf -docnum 1 %s > %t.so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t.so.o + +# Build executable +# RUN: yaml2obj -format=elf -docnum 2 %s > %t.o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t.o.o %t.so +# RUN: llvm-objdump -d %t.exe | FileCheck %s + +# CHECK: Disassembly of section .plt: +# CHECK-NEXT: .plt: +# CHECK-NEXT: 400170: 40 00 1c 3c lui $gp, 64 +# CHECK-NEXT: 400174: 00 20 99 8f lw $25, 8192($gp) +# CHECK-NEXT: 400178: 00 20 9c 27 addiu $gp, $gp, 8192 +# CHECK-NEXT: 40017c: 23 c0 1c 03 subu $24, $24, $gp +# CHECK-NEXT: 400180: 21 78 e0 03 move $15, $ra +# CHECK-NEXT: 400184: 82 c0 18 00 srl $24, $24, 2 +# CHECK-NEXT: 400188: 09 f8 20 03 jalr $25 +# CHECK-NEXT: 40018c: fe ff 18 27 addiu $24, $24, -2 +# CHECK-NEXT: 400190: 40 00 0f 3c lui $15, 64 +# CHECK-NEXT: 400194: 08 20 f9 8d lw $25, 8200($15) +# CHECK-NEXT: 400198: 08 00 20 03 jr $25 +# CHECK-NEXT: 40019c: 08 20 f8 25 addiu $24, $15, 8200 + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, + EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "0000000C00000000" + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x0 + Symbol: T1 + Type: R_MIPS_26 + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: T1 + Type: R_MICROMIPS_HI16 + - Offset: 0x00 + Symbol: T1 + Type: R_MICROMIPS_LO16 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 0x8 + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 8 + - Name: T1 +... diff --git a/test/elf/Mips/plt-entry-mixed-2.test b/test/elf/Mips/plt-entry-mixed-2.test new file mode 100644 index 0000000000000..5aec7d7a328a0 --- /dev/null +++ b/test/elf/Mips/plt-entry-mixed-2.test @@ -0,0 +1,93 @@ +# REQUIRES: mips + +# Conditions: +# a) Object file contains both R_MIPS_26 and R_MICROMIPS_26_S1 relocations. +# Check: +# a) PLT contains both regular and compressed PLT entries + +# Build shared library +# RUN: yaml2obj -format=elf -docnum 1 %s > %t.so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t.so.o + +# Build executable +# RUN: yaml2obj -format=elf -docnum 2 %s > %t.o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t.o.o %t.so +# RUN: llvm-objdump -s %t.exe | FileCheck %s + +# FIXME (simon): Check the disassembler output when llvm-objdump starts +# to support microMIPS instruction encoding. + +# CHECK: Contents of section .plt: +# CHECK-NEXT: 400170 40001c3c 0020998f 00209c27 23c01c03 @..<. ... .'#... +# CHECK-NEXT: 400180 2178e003 82c01800 09f82003 feff1827 !x........ ....' +# CHECK-NEXT: 400190 40000f3c 0820f98d 08002003 0820f825 @..<. .... .. .% +# CHECK-NEXT: 4001a0 00799a07 22ff0000 9945020f .y.."....E.. + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T2 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, + EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "0000000C000000000000000000000000" + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x0 + Symbol: T2 + Type: R_MIPS_26 + - Offset: 0x8 + Symbol: T2 + Type: R_MICROMIPS_26_S1 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 0x8 + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x8 + Size: 0x8 + Other: [STO_MIPS_MICROMIPS] + - Name: T2 +... diff --git a/test/elf/Mips/plt-entry-mixed-3.test b/test/elf/Mips/plt-entry-mixed-3.test new file mode 100644 index 0000000000000..c61991b4b1395 --- /dev/null +++ b/test/elf/Mips/plt-entry-mixed-3.test @@ -0,0 +1,98 @@ +# REQUIRES: mips + +# Conditions: +# a) Object file contains microMIPS instructions. +# b) There is a relocation refers arbitrary symbols and requires a PLT entry. +# Check: +# a) PLT contains a compressed entry. + +# Build shared library +# RUN: yaml2obj -format=elf -docnum 1 %s > %t.so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t.so.o + +# Build executable +# RUN: yaml2obj -format=elf -docnum 2 %s > %t.o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t.o.o %t.so +# RUN: llvm-objdump -s %t.exe | FileCheck %s + +# FIXME (simon): Check the disassembler output when llvm-objdump starts +# to support microMIPS instruction encoding. + +# CHECK: Contents of section .plt: +# CHECK-NEXT: 400170 8079a407 23ff0000 35052525 0233feff .y..#...5.%%.3.. +# CHECK-NEXT: 400180 ff0df945 830f000c 0079a007 22ff0000 ...E.....y.."... +# CHECK-NEXT: 400190 9945020f .E.. + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, + EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 16 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: T1 + Type: R_MICROMIPS_HI16 + - Offset: 0x00 + Symbol: T1 + Type: R_MICROMIPS_LO16 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 16 + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 8 + - Name: T1 +... diff --git a/test/elf/Mips/plt-entry-mixed-4.test b/test/elf/Mips/plt-entry-mixed-4.test new file mode 100644 index 0000000000000..acf5060621f6c --- /dev/null +++ b/test/elf/Mips/plt-entry-mixed-4.test @@ -0,0 +1,85 @@ +# REQUIRES: mips + +# Conditions: +# a) Object file contains R_MIPS_26 relocation refers to the microMIPS symbol. +# Check: +# a) PLT contains a regular non-compressed entry. + +# Build shared library +# RUN: yaml2obj -format=elf -docnum 1 %s > %t.so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t.so.o + +# Build executable +# RUN: yaml2obj -format=elf -docnum 2 %s > %t.o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t.o.o %t.so +# RUN: llvm-objdump -s %t.exe | FileCheck %s + +# FIXME (simon): Check the disassembler output when llvm-objdump starts +# to support microMIPS instruction encoding. + +# CHECK: Contents of section .plt: +# CHECK-NEXT: 400170 40001c3c 0020998f 00209c27 23c01c03 @..<. ... .'#... +# CHECK-NEXT: 400180 2178e003 82c01800 09f82003 feff1827 !x........ ....' +# CHECK-NEXT: 400190 40000f3c 0820f98d 08002003 0820f825 @..<. .... .. .% + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, + EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + Other: [STO_MIPS_MICROMIPS] + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, + EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "0000000C00000000" + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x0 + Symbol: T1 + Type: R_MIPS_26 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 0x8 + - Name: T1 +... diff --git a/test/elf/Mips/plt-entry-r6.test b/test/elf/Mips/plt-entry-r6.test new file mode 100644 index 0000000000000..bb05531487ff5 --- /dev/null +++ b/test/elf/Mips/plt-entry-r6.test @@ -0,0 +1,109 @@ +# REQUIRES: mips + +# Check generation of PLT entries in case of R6 target ABI. + +# Build shared library +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o + +# Build executable +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o.o %t.so +# RUN: llvm-objdump -d %t.exe | FileCheck %s + +# CHECK: Disassembly of section .plt: +# CHECK-NEXT: .plt: +# CHECK-NEXT: 400160: 40 00 1c 3c lui $gp, 64 +# CHECK-NEXT: 400164: 00 20 99 8f lw $25, 8192($gp) +# CHECK-NEXT: 400168: 00 20 9c 27 addiu $gp, $gp, 8192 +# CHECK-NEXT: 40016c: 23 c0 1c 03 subu $24, $24, $gp +# CHECK-NEXT: 400170: 21 78 e0 03 move $15, $ra +# CHECK-NEXT: 400174: 82 c0 18 00 srl $24, $24, 2 +# CHECK-NEXT: 400178: 09 f8 20 03 jalr $25 +# CHECK-NEXT: 40017c: fe ff 18 27 addiu $24, $24, -2 +# CHECK-NEXT: 400180: 40 00 0f 3c lui $15, 64 +# CHECK-NEXT: 400184: 08 20 f9 8d lw $25, 8200($15) +# CHECK-NEXT: 400188: 09 00 20 03 jr $25 +# CHECK-NEXT: 40018c: 08 20 f8 25 addiu $24, $15, 8200 + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R6] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R6] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "0000000C00000000" + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x0 + Symbol: T1 + Type: R_MIPS_26 + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_HI16 + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_LO16 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 0x8 + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 8 + - Name: T1 +... diff --git a/test/elf/Mips/plt-header-micro.test b/test/elf/Mips/plt-header-micro.test new file mode 100644 index 0000000000000..ce042741cd326 --- /dev/null +++ b/test/elf/Mips/plt-header-micro.test @@ -0,0 +1,108 @@ +# REQUIRES: mips + +# Check initialization of .plt header entries +# if all PLT entries use microMIPS encoding. + +# Build shared library +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o + +# Build executable +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e glob -o %t.exe %t-o.o %t.so +# RUN: llvm-objdump -d -mattr=micromips %t.exe | FileCheck -check-prefix=DIS %s +# RUN: llvm-objdump -section-headers %t.exe | FileCheck -check-prefix=EXE %s + +# DIS: Disassembly of section .plt: +# DIS-NEXT: .plt: +# DIS-NEXT: 400170: 80 79 a4 07 addiupc $3, 7824 +# DIS-NEXT: 400174: 23 ff 00 00 lw $25, 0($3) +# DIS-NEXT: 400178: 35 05 subu16 $2, $2, $3 +# DIS-NEXT: 40017a: 25 25 srl16 $2, $2, 2 +# DIS-NEXT: 40017c: 02 33 fe ff addiu $24, $2, -2 +# DIS-NEXT: 400180: ff 0d move $15, $ra +# DIS-NEXT: 400182: f9 45 jalrs16 $25 +# DIS-NEXT: 400184: 83 0f move $gp, $3 +# DIS-NEXT: 400186: 00 0c nop + +# DIS-NEXT: 400188: 00 79 a0 07 addiupc $2, 7808 +# DIS-NEXT: 40018c: 22 ff 00 00 lw $25, 0($2) +# DIS-NEXT: 400190: 99 45 jr16 $25 +# DIS-NEXT: 400192: 02 0f move $24, $2 + +# EXE: Sections: +# EXE: Idx Name Size Address Type +# EXE: 6 .plt 00000024 0000000000400170 TEXT DATA +# EXE: 10 .got.plt 0000000c 0000000000402000 DATA + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Content: '000000000000000000f4000000000000f400000000000000f400000000000000' +# jal .text jal glob jal T1 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x08 + Symbol: .text + Type: R_MICROMIPS_26_S1 + - Offset: 0x10 + Symbol: glob + Type: R_MICROMIPS_26_S1 + - Offset: 0x18 + Symbol: T1 + Type: R_MICROMIPS_26_S1 + +Symbols: + Local: + - Name: loc + Section: .text + Value: 0x10 + Other: [ STO_MIPS_MICROMIPS ] + - Name: .text + Type: STT_SECTION + Section: .text + Global: + - Name: glob + Section: .text + Other: [ STO_MIPS_MICROMIPS ] + - Name: T1 +... diff --git a/test/elf/Mips/plt-header-mixed.test b/test/elf/Mips/plt-header-mixed.test new file mode 100644 index 0000000000000..0d3af19ee5939 --- /dev/null +++ b/test/elf/Mips/plt-header-mixed.test @@ -0,0 +1,105 @@ +# REQUIRES: mips + +# Check initialization of .plt header entries if there are both regular +# and microMIPS encoded PLT entries. Check that R_MIPS_26 and R_MICROMIPS_26_S1 +# relocation with the same target cause generation of two distinct PLT entries. + +# Build shared library +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o + +# Build executable +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e globR -o %t.exe %t-o.o %t.so +# RUN: llvm-objdump -d %t.exe | FileCheck -check-prefix=DIS %s +# RUN: llvm-objdump -section-headers %t.exe | FileCheck -check-prefix=EXE %s + +# DIS: Disassembly of section .plt: +# DIS-NEXT: .plt: +# DIS-NEXT: 400170: 40 00 1c 3c lui $gp, 64 +# DIS-NEXT: 400174: 00 20 99 8f lw $25, 8192($gp) +# DIS-NEXT: 400178: 00 20 9c 27 addiu $gp, $gp, 8192 +# DIS-NEXT: 40017c: 23 c0 1c 03 subu $24, $24, $gp +# DIS-NEXT: 400180: 21 78 e0 03 move $15, $ra +# DIS-NEXT: 400184: 82 c0 18 00 srl $24, $24, 2 +# DIS-NEXT: 400188: 09 f8 20 03 jalr $25 +# DIS-NEXT: 40018c: fe ff 18 27 addiu $24, $24, -2 + +# DIS-NEXT: 400190: 40 00 0f 3c lui $15, 64 +# DIS-NEXT: 400194: 08 20 f9 8d lw $25, 8200($15) +# DIS-NEXT: 400198: 08 00 20 03 jr $25 +# DIS-NEXT: 40019c: 08 20 f8 25 addiu $24, $15, 8200 + +# FIXME (simon): Check micromips PLT entry +# DIS-NEXT: 4001a8: 99 45 02 0f jal 201922148 + +# EXE: Sections: +# EXE: Idx Name Size Address Type +# EXE: 6 .plt 0000003c 0000000000400170 TEXT DATA +# EXE: 10 .got.plt 0000000c 0000000000402000 DATA + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x8 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x0 + Symbol: T1 + Type: R_MIPS_26 + - Offset: 0x4 + Symbol: T1 + Type: R_MICROMIPS_26_S1 + +Symbols: + Global: + - Name: globR + Section: .text + Value: 0x0 + Size: 0x4 + - Name: globM + Section: .text + Value: 0x4 + Size: 0x4 + Other: [ STO_MIPS_MICROMIPS ] + - Name: T1 +... diff --git a/test/elf/Mips/plt-header.test b/test/elf/Mips/plt-header.test new file mode 100644 index 0000000000000..caf7e83ea5e24 --- /dev/null +++ b/test/elf/Mips/plt-header.test @@ -0,0 +1,99 @@ +# REQUIRES: mips + +# Check initialization of .plt header entries. + +# Build shared library +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o + +# Build executable +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e glob -o %t.exe %t-o.o %t.so +# RUN: llvm-objdump -section-headers -disassemble %t.exe | \ +# RUN: FileCheck -check-prefix=EXE %s + +# EXE: Disassembly of section .plt: +# EXE: .plt: +# PLT0 entry. Points to the .got.plt[0] +# EXE-NEXT: 400160: 40 00 1c 3c lui $gp, 64 +# EXE-NEXT: 400164: 00 20 99 8f lw $25, 8192($gp) +# EXE-NEXT: 400168: 00 20 9c 27 addiu $gp, $gp, 8192 +# EXE-NEXT: 40016c: 23 c0 1c 03 subu $24, $24, $gp +# EXE-NEXT: 400170: 21 78 e0 03 move $15, $ra +# EXE-NEXT: 400174: 82 c0 18 00 srl $24, $24, 2 +# EXE-NEXT: 400178: 09 f8 20 03 jalr $25 +# EXE-NEXT: 40017c: fe ff 18 27 addiu $24, $24, -2 + +# EXE: Sections: +# EXE: Idx Name Size Address Type +# EXE: 6 .plt 00000030 0000000000400160 TEXT DATA +# EXE: 10 .got.plt 0000000c 0000000000402000 DATA + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x20 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x08 + Symbol: .text + Type: R_MIPS_26 + - Offset: 0x10 + Symbol: glob + Type: R_MIPS_26 + - Offset: 0x18 + Symbol: T1 + Type: R_MIPS_26 + +Symbols: + Local: + - Name: loc + Section: .text + Value: 0x10 + - Name: .text + Type: STT_SECTION + Section: .text + Global: + - Name: glob + Section: .text + - Name: T1 +... diff --git a/test/elf/Mips/r26-1-micro.test b/test/elf/Mips/r26-1-micro.test new file mode 100644 index 0000000000000..629754febc7e3 --- /dev/null +++ b/test/elf/Mips/r26-1-micro.test @@ -0,0 +1,131 @@ +# REQUIRES: mips + +# Check handling of R_MICROMIPS_26_S1 relocation. + +# Build shared library +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o + +# Build executable +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: llvm-readobj -relocations %t-o.o | \ +# RUN: FileCheck -check-prefix=OBJ-REL %s +# RUN: lld -flavor gnu -target mipsel -e glob -o %t.exe %t-o.o %t.so +# RUN: llvm-readobj -relocations %t.exe | FileCheck -check-prefix=EXE-REL %s +# RUN: llvm-objdump -section-headers %t.exe | FileCheck -check-prefix=EXE %s +# RUN: llvm-objdump -s -d -mattr=micromips %t.exe | \ +# RUN: FileCheck -check-prefix=DIS %s + +# Object file has three R_MICROMIPS_26_S1 relocations +# OBJ-REL: Relocations [ +# OBJ-REL-NEXT: Section (2) .rel.text { +# OBJ-REL-NEXT: 0x8 R_MICROMIPS_26_S1 loc 0x0 +# OBJ-REL-NEXT: 0x10 R_MICROMIPS_26_S1 glob 0x0 +# OBJ-REL-NEXT: 0x18 R_MICROMIPS_26_S1 T1 0x0 +# OBJ-REL-NEXT: } +# OBJ-REL-NEXT: ] + +# Executable file has the only relocation for external symbol +# EXE-REL: Relocations [ +# EXE-REL-NEXT: Section (5) .rel.plt { +# EXE-REL-NEXT: 0x402008 R_MIPS_JUMP_SLOT T1 0x0 +# EXE-REL-NEXT: } +# EXE-REL-NEXT: ] + +# EXE: Sections: +# EXE: Idx Name Size Address Type +# EXE: 6 .plt 00000024 0000000000400160 TEXT DATA +# EXE: 10 .got.plt 0000000c 0000000000402000 DATA + +# DIS: Disassembly of section .plt: +# DIS-NEXT: .plt: +# DIS-NEXT: 400160: 80 79 a8 07 addiupc $3, 7840 +# DIS-NEXT: 400164: 23 ff 00 00 lw $25, 0($3) +# DIS-NEXT: 400168: 35 05 subu16 $2, $2, $3 +# DIS-NEXT: 40016a: 25 25 srl16 $2, $2, 2 +# DIS-NEXT: 40016c: 02 33 fe ff addiu $24, $2, -2 +# DIS-NEXT: 400170: ff 0d move $15, $ra +# DIS-NEXT: 400172: f9 45 jalrs16 $25 +# DIS-NEXT: 400174: 83 0f move $gp, $3 +# DIS-NEXT: 400176: 00 0c nop + +# DIS-NEXT: 400178: 00 79 a4 07 addiupc $2, 7824 +# DIS-NEXT: 40017c: 22 ff 00 00 lw $25, 0($2) +# DIS-NEXT: 400180: 99 45 jr16 $25 +# DIS-NEXT: 400182: 02 0f move $24, $2 + +# DIS: Contents of section .text: +# DIS-NEXT: 400184 09f82003 00000000 2400ca0c 00000000 .. .....$....... +# DIS-NEXT: 400194 2000c20c 00000000 2000bc0c 00000000 ....... ....... + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_CPIC, EF_MIPS_ABI_O32, + EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Content: '09F82003000000000400000C000000000000000C000000000000000C00000000' + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x08 + Symbol: loc + Type: R_MICROMIPS_26_S1 + - Offset: 0x10 + Symbol: glob + Type: R_MICROMIPS_26_S1 + - Offset: 0x18 + Symbol: T1 + Type: R_MICROMIPS_26_S1 + +Symbols: + Local: + - Name: loc + Section: .text + Value: 0x10 + Other: [ STO_MIPS_MICROMIPS ] + - Name: .text + Type: STT_SECTION + Section: .text + Global: + - Name: glob + Section: .text + Other: [ STO_MIPS_MICROMIPS ] + - Name: T1 +... diff --git a/test/elf/Mips/r26-1.test b/test/elf/Mips/r26-1.test new file mode 100644 index 0000000000000..abc0a7ce5a811 --- /dev/null +++ b/test/elf/Mips/r26-1.test @@ -0,0 +1,132 @@ +# REQUIRES: mips + +# Check handling of R_MIPS_26 relocation. + +# Build shared library +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o + +# Build executable +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: llvm-readobj -relocations %t-o.o | \ +# RUN: FileCheck -check-prefix=OBJ-REL %s +# RUN: lld -flavor gnu -target mipsel -e glob -o %t.exe %t-o.o %t.so +# RUN: llvm-objdump -section-headers -disassemble %t.exe | \ +# RUN: FileCheck -check-prefix=EXE %s +# RUN: llvm-readobj -relocations %t.exe | FileCheck -check-prefix=EXE-REL %s + +# Object file has three R_MIPS_26 relocations +# OBJ-REL: Relocations [ +# OBJ-REL-NEXT: Section (2) .rel.text { +# OBJ-REL-NEXT: 0x8 R_MIPS_26 .text 0x0 +# OBJ-REL-NEXT: 0x10 R_MIPS_26 glob 0x0 +# OBJ-REL-NEXT: 0x18 R_MIPS_26 T1 0x0 +# OBJ-REL-NEXT: } +# OBJ-REL-NEXT: ] + +# Executable file has the only relocation for external symbol +# EXE-REL: Relocations [ +# EXE-REL-NEXT: Section (5) .rel.plt { +# EXE-REL-NEXT: 0x402008 R_MIPS_JUMP_SLOT T1 0x0 +# EXE-REL-NEXT: } +# EXE-REL-NEXT: ] + +# EXE: Disassembly of section .plt: +# EXE: .plt: +# PLTA entry. Points to the .got.plt[1] +# EXE: 400180: 40 00 0f 3c lui $15, 64 +# EXE-NEXT: 400184: 08 20 f9 8d lw $25, 8200($15) +# EXE-NEXT: 400188: 08 00 20 03 jr $25 +# EXE-NEXT: 40018c: 08 20 f8 25 addiu $24, $15, 8200 + +# EXE: Disassembly of section .text: +# EXE: glob: +# EXE-NEXT: 400190: 09 f8 20 03 jalr $25 +# EXE-NEXT: 400194: 00 00 00 00 nop +# +# Jump to 'loc' label address +# EXE-NEXT: 400198: 68 00 10 0c jal 4194720 +# EXE-NEXT: 40019c: 00 00 00 00 nop +# +# EXE: loc: +# Jump to 'glob' label address +# EXE-NEXT: 4001a0: 64 00 10 0c jal 4194704 +# EXE-NEXT: 4001a4: 00 00 00 00 nop +# +# Jump to the first PLT entry (.plt + 32) for T1 entry +# EXE-NEXT: 4001a8: 60 00 10 0c jal 4194688 +# EXE-NEXT: 4001ac: 00 00 00 00 nop + +# EXE: Sections: +# EXE: Idx Name Size Address Type +# EXE: 6 .plt 00000030 0000000000400160 TEXT DATA +# EXE: 10 .got.plt 0000000c 0000000000402000 DATA + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32 ] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Content: '09F82003000000000400000C000000000000000C000000000000000C00000000' + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x08 + Symbol: .text + Type: R_MIPS_26 + - Offset: 0x10 + Symbol: glob + Type: R_MIPS_26 + - Offset: 0x18 + Symbol: T1 + Type: R_MIPS_26 + +Symbols: + Local: + - Name: loc + Section: .text + Value: 0x10 + - Name: .text + Type: STT_SECTION + Section: .text + Global: + - Name: glob + Section: .text + - Name: T1 +... diff --git a/test/elf/Mips/r26-2-micro.test b/test/elf/Mips/r26-2-micro.test new file mode 100644 index 0000000000000..b92b9acf4c842 --- /dev/null +++ b/test/elf/Mips/r26-2-micro.test @@ -0,0 +1,88 @@ +# REQUIRES: mips + +# Check reading addendum for R_MICROMIPS_26_S1 relocation. +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: lld -flavor gnu -target mipsel -o %t-exe %t-obj +# RUN: llvm-objdump -d -mattr=micromips %t-exe | FileCheck -check-prefix=DIS %s +# RUN: llvm-objdump -t %t-exe | FileCheck %s + +# DIS: Disassembly of section .text: +# DIS-NEXT: loc0: +# DIS-NEXT: 400110: 00 00 00 00 nop + +# DIS: __start: +# DIS-NEXT: 400114: 20 f4 8e e0 jal 4309276 +# DIS-NEXT: 400118: 00 00 00 00 nop +# DIS-NEXT: 40011c: 20 f4 9a e0 jal 4309300 +# DIS-NEXT: 400120: 00 00 00 00 nop + +# DIS: loc1: +# DIS-NEXT: 400124: 20 f4 89 00 jal 4194578 +# DIS-NEXT: 400128: 00 00 00 00 nop +# DIS-NEXT: 40012c: 20 f4 91 00 jal 4194594 +# DIS-NEXT: 400130: 00 00 00 00 nop + +# CHECK: SYMBOL TABLE: +# CHECK: 00400124 l F .text 00000010 loc1 +# CHECK: 00400114 g F .text 00000010 __start + +!ELF +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS] + +Sections: +- Name: .text + Type: SHT_PROGBITS +# nop +# jal __start + 0x1C000 +# nop +# jal loc + 0x1C000 +# nop +# jal __start + 7FFFFE2 +# nop +# jal loc + 7FFFFEA +# nop + Content: "0000000000f404e00000000000f408e000000000FFF7FFFF00000000FFF7FFFF00000000" + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x4 + Symbol: __start + Type: R_MICROMIPS_26_S1 + - Offset: 0xC + Symbol: loc1 + Type: R_MICROMIPS_26_S1 + - Offset: 0x14 + Symbol: __start + Type: R_MICROMIPS_26_S1 + - Offset: 0x1C + Symbol: loc1 + Type: R_MICROMIPS_26_S1 + +Symbols: + Global: + - Name: __start + Section: .text + Value: 0x4 + Size: 8 + Other: [ STO_MIPS_MICROMIPS ] + Local: + - Name: loc0 + Section: .text + Value: 0 + Size: 4 + Other: [ STO_MIPS_MICROMIPS ] + - Name: loc1 + Section: .text + Value: 0x14 + Size: 8 + Other: [ STO_MIPS_MICROMIPS ] diff --git a/test/elf/Mips/r26-2.test b/test/elf/Mips/r26-2.test new file mode 100644 index 0000000000000..0f0063ae13349 --- /dev/null +++ b/test/elf/Mips/r26-2.test @@ -0,0 +1,82 @@ +# REQUIRES: mips + +# Check reading addendum for R_MIPS_26 relocation. +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: lld -flavor gnu -target mipsel -o %t-exe %t-obj +# RUN: llvm-objdump -t -disassemble %t-exe | FileCheck %s + +# CHECK: Disassembly of section .text: +# CHECK-NEXT: __start: +# CHECK-NEXT: 400110: 00 00 00 00 nop +# CHECK-NEXT: 400114: 44 70 10 0c jal 4309264 +# 0x107044 << 2 = 0x41C110 = _start + (0x7000 << 2) +# CHECK-NEXT: 400118: 00 00 00 00 nop +# +# CHECK: loc: +# CHECK-NEXT: 40011c: 47 70 10 0c jal 4309276 +# 0x107047 << 2 = 0x41C11C = loc + (0x7000 << 2) +# CHECK-NEXT: 400120: 00 00 00 00 nop +# CHECK-NEXT: 400124: 43 00 10 0c jal 4194572 +# 0x100043 << 2 = 0x40010C = _start - 4 +# CHECK-NEXT: 400128: 00 00 00 00 nop +# CHECK-NEXT: 40012c: 46 00 10 0c jal 4194584 +# 0x100046 << 2 = 0x400118 = loc - 4 +# CHECK-NEXT: 400130: 00 00 00 00 nop + +# CHECK: SYMBOL TABLE: +# CHECK: 0040011c l F .text 00000018 loc +# CHECK: 00400110 g F .text 0000000c __start + +!ELF +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS +# nop +# jal __start + 0x1C000 +# nop +# jal loc + 0x1C000 +# nop +# jal __start - 1 +# nop +# jal loc - 1 +# nop + Content: "000000000070000C000000000070000C00000000FFFFFF0F00000000FFFFFF0F00000000" + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x4 + Symbol: __start + Type: R_MIPS_26 + - Offset: 0xC + Symbol: loc + Type: R_MIPS_26 + - Offset: 0x14 + Symbol: __start + Type: R_MIPS_26 + - Offset: 0x1C + Symbol: loc + Type: R_MIPS_26 + +Symbols: + Global: + - Name: __start + Section: .text + Value: 0x0 + Size: 4 + Local: + - Name: loc + Section: .text + Value: 0xc + Size: 4 diff --git a/test/elf/Mips/rel-32.test b/test/elf/Mips/rel-32.test new file mode 100644 index 0000000000000..beefde59b7b1c --- /dev/null +++ b/test/elf/Mips/rel-32.test @@ -0,0 +1,59 @@ +# Check handling of R_MIPS_32 relocation. +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: lld -flavor gnu -target mipsel -o %t-exe %t-obj +# RUN: llvm-objdump -s -t %t-exe | FileCheck %s + +# CHECK: Contents of section .data: +# CHECK-NEXT: 402000 00000000 09204080 05204080 ..... @.. @. +# ^^ data2 + 0x80000001 = 0x80402009 +# ^^ data1 + 0x80000001 = 0x80402005 +# CHECK: SYMBOL TABLE: +# CHECK: 00402004 g .data 00000004 data1 +# CHECK: 00402008 g .data 00000004 data2 + +!ELF +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "00000000" + AddressAlign: 16 + Flags: [SHF_ALLOC] +- Name: .data + Type: SHT_PROGBITS + Content: "000000000100008001000080" + AddressAlign: 16 + Flags: [SHF_ALLOC, SHF_WRITE] + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x4 + Symbol: data2 + Type: R_MIPS_32 + - Offset: 0x8 + Symbol: data1 + Type: R_MIPS_32 + +Symbols: + Global: + - Name: __start + Section: .text + Value: 0x0 + Size: 4 + - Name: data1 + Section: .data + Value: 0x4 + Size: 4 + - Name: data2 + Section: .data + Value: 0x8 + Size: 4 diff --git a/test/elf/Mips/rel-64.test b/test/elf/Mips/rel-64.test new file mode 100644 index 0000000000000..e05b75687038e --- /dev/null +++ b/test/elf/Mips/rel-64.test @@ -0,0 +1,61 @@ +# Check handling of R_MIPS_64 relocation. + +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mips64el -o %t.exe %t.o +# RUN: llvm-objdump -s -t %t.exe | FileCheck %s + +# CHECK: Contents of section .data: +# CHECK-NEXT: 120002000 d1010020 01000000 d0010020 01000100 ... ....... .... +# ^^ __start + 1 = 0x1200001d1 +# ^^ __start + 0x1000000000000 +# = 0x10001200001d0 +# CHECK: SYMBOL TABLE: +# CHECK: 00000001200001d0 g .rodata 00000008 __start + +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ARCH_64] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_ALLOC] +- Name: .data + Type: SHT_PROGBITS + Size: 0x10 + AddressAlign: 16 + Flags: [SHF_ALLOC, SHF_WRITE] + +- Name: .rela.data + Type: SHT_RELA + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x0 + Symbol: __start + Type: R_MIPS_64 + Addend: 1 + - Offset: 0x8 + Symbol: __start + Type: R_MIPS_64 + Addend: 0x1000000000000 + +Symbols: + Global: + - Name: __start + Section: .text + Value: 0x0 + Size: 8 + - Name: data1 + Section: .data + Value: 0x0 + Size: 8 + - Name: data2 + Section: .data + Value: 0x8 + Size: 8 diff --git a/test/elf/Mips/rel-copy-micro.test b/test/elf/Mips/rel-copy-micro.test new file mode 100644 index 0000000000000..afa99198e37f5 --- /dev/null +++ b/test/elf/Mips/rel-copy-micro.test @@ -0,0 +1,159 @@ +# Check R_MIPS_COPY relocation emitting +# when linking non-shared executable file. +# +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so1.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t1.so %t-so1.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-so2.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t2.so %t-so2.o +# RUN: yaml2obj -format=elf -docnum 3 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o.o %t1.so %t2.so +# RUN: llvm-readobj -dt -r -dynamic-table %t.exe | FileCheck %s + +# CHECK: Relocations [ +# CHECK-NEXT: Section (5) .rel.dyn { +# CHECK-NEXT: 0x402010 R_MIPS_COPY D1 0x0 +# CHECK-NEXT: 0x402018 R_MIPS_COPY D2 0x0 +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# CHECK: DynamicSymbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: @ (0) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local (0x0) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: D1@ (1) +# CHECK-NEXT: Value: 0x402010 +# CHECK-NEXT: Size: 8 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Object (0x1) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .bss (0xA) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: D2@ (4) +# CHECK-NEXT: Value: 0x402018 +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Object (0x1) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .bss (0xA) +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# CHECK: DynamicSection [ ({{.*}} entries) +# CHECK: 0x00000001 NEEDED SharedLibrary (rel-copy-micro.test.tmp1.so) +# CHECK: 0x00000001 NEEDED SharedLibrary (rel-copy-micro.test.tmp2.so) +# CHECK-NEXT: 0x00000000 NULL 0x0 +# CHECK-NEXT: ] + +# so1.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +Symbols: + Global: + - Name: D1 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 4 + +# so2.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .data + Type: SHT_PROGBITS + Size: 0x04 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +Symbols: + Global: + - Name: D2 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: D1 + Type: R_MICROMIPS_HI16 + - Offset: 0x00 + Symbol: D1 + Type: R_MICROMIPS_LO16 + - Offset: 0x08 + Symbol: D2 + Type: R_MICROMIPS_HI16 + - Offset: 0x08 + Symbol: D2 + Type: R_MICROMIPS_LO16 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 8 + Other: [ STO_MIPS_MICROMIPS ] + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 8 + - Name: D1 + Type: STT_OBJECT + - Name: D2 +... diff --git a/test/elf/Mips/rel-copy-pc.test b/test/elf/Mips/rel-copy-pc.test new file mode 100644 index 0000000000000..2dd702334fed8 --- /dev/null +++ b/test/elf/Mips/rel-copy-pc.test @@ -0,0 +1,113 @@ +# Check R_MIPS_COPY relocation emitting caused by R_MIPS_PCHI16 / R_MIPS_PCLO16 +# relocations when linking non-shared executable file. +# +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o.o %t.so +# RUN: llvm-readobj -dt -r -dynamic-table %t.exe | FileCheck %s + +# CHECK: Relocations [ +# CHECK-NEXT: Section (5) .rel.dyn { +# CHECK-NEXT: 0x402008 R_MIPS_COPY D1 0x0 +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# CHECK: DynamicSymbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: @ (0) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local (0x0) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: D1@ (1) +# CHECK-NEXT: Value: 0x402008 +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Object (0x1) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .bss (0xA) +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# CHECK: DynamicSection [ ({{.*}} entries) +# CHECK: 0x00000001 NEEDED SharedLibrary (rel-copy-pc.test.tmp.so) +# CHECK-NEXT: 0x00000000 NULL 0x0 +# CHECK-NEXT: ] + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R6] + +Sections: +- Name: .data + Type: SHT_PROGBITS + Size: 4 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +Symbols: + Global: + - Name: D1 + Section: .data + Type: STT_OBJECT + Value: 0 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R6] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 4 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 4 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0 + Symbol: D1 + Type: R_MIPS_PCHI16 + - Offset: 0 + Symbol: D1 + Type: R_MIPS_PCLO16 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0 + Size: 4 + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0 + Size: 4 + - Name: D1 +... diff --git a/test/elf/Mips/rel-copy.test b/test/elf/Mips/rel-copy.test new file mode 100644 index 0000000000000..648a25e22ae78 --- /dev/null +++ b/test/elf/Mips/rel-copy.test @@ -0,0 +1,177 @@ +# Check R_MIPS_COPY relocation emitting +# when linking non-shared executable file. +# +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so1.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t1.so %t-so1.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-so2.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t2.so %t-so2.o +# RUN: yaml2obj -format=elf -docnum 3 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o.o %t1.so %t2.so +# RUN: llvm-readobj -dt -r -dynamic-table %t.exe | FileCheck %s + +# CHECK: Relocations [ +# CHECK-NEXT: Section (5) .rel.dyn { +# CHECK-NEXT: 0x402004 R_MIPS_REL32 D2 0x0 +# CHECK-NEXT: 0x402010 R_MIPS_COPY D1 0x0 +# CHECK-NEXT: 0x402018 R_MIPS_COPY D3 0x0 +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# CHECK: DynamicSymbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: @ (0) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local (0x0) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: D1@ (1) +# CHECK-NEXT: Value: 0x402010 +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Object (0x1) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .bss (0xA) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: D3@ (4) +# CHECK-NEXT: Value: 0x402018 +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Object (0x1) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .bss (0xA) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: D2@ (7) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Object (0x1) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# CHECK: DynamicSection [ ({{.*}} entries) +# CHECK: 0x00000001 NEEDED SharedLibrary (rel-copy.test.tmp1.so) +# CHECK: 0x00000001 NEEDED SharedLibrary (rel-copy.test.tmp2.so) +# CHECK-NEXT: 0x00000000 NULL 0x0 +# CHECK-NEXT: ] + +# so1.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +Symbols: + Global: + - Name: D1 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 4 + - Name: D2 + Section: .data + Type: STT_OBJECT + Value: 0x4 + Size: 4 + +# so2.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .data + Type: SHT_PROGBITS + Size: 0x04 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +Symbols: + Global: + - Name: D3 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: D1 + Type: R_MIPS_HI16 + - Offset: 0x00 + Symbol: D1 + Type: R_MIPS_LO16 + - Offset: 0x04 + Symbol: D2 + Type: R_MIPS_32 + - Offset: 0x08 + Symbol: D3 + Type: R_MIPS_HI16 + - Offset: 0x08 + Symbol: D3 + Type: R_MIPS_LO16 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 8 + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 8 + - Name: D1 + Type: STT_OBJECT + - Name: D2 + - Name: D3 +... diff --git a/test/elf/Mips/rel-dynamic-01-micro.test b/test/elf/Mips/rel-dynamic-01-micro.test new file mode 100644 index 0000000000000..6e0a41bf26063 --- /dev/null +++ b/test/elf/Mips/rel-dynamic-01-micro.test @@ -0,0 +1,201 @@ +# REQUIRES: mips + +# Conditions: +# a) Linking a non-shared executable file. +# b) Relocations' targets are symbols defined in the shared object. +# Check: +# a) Emitting R_MIPS_COPY, R_MIPS_JUMP_SLOT relocations. +# b) PLT entries creation. +# c) STO_MIPS_PLT flag in the dynamic symbol table for symbols require +# a pointer equality. +# +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o.o %t.so +# RUN: llvm-objdump -d -mattr=micromips %t.exe | FileCheck -check-prefix=DIS %s +# RUN: llvm-readobj -dt -r %t.exe | FileCheck -check-prefix=PLT-SYM %s + +# DIS: Disassembly of section .plt: +# DIS-NEXT: .plt: +# DIS-NEXT: 4001b0: 80 79 94 07 addiupc $3, 7760 +# DIS-NEXT: 4001b4: 23 ff 00 00 lw $25, 0($3) +# DIS-NEXT: 4001b8: 35 05 subu16 $2, $2, $3 +# DIS-NEXT: 4001ba: 25 25 srl16 $2, $2, 2 +# DIS-NEXT: 4001bc: 02 33 fe ff addiu $24, $2, -2 +# DIS-NEXT: 4001c0: ff 0d move $15, $ra +# DIS-NEXT: 4001c2: f9 45 jalrs16 $25 +# DIS-NEXT: 4001c4: 83 0f move $gp, $3 +# DIS-NEXT: 4001c6: 00 0c nop + +# DIS-NEXT: 4001c8: 00 79 90 07 addiupc $2, 7744 +# DIS-NEXT: 4001cc: 22 ff 00 00 lw $25, 0($2) +# DIS-NEXT: 4001d0: 99 45 jr16 $25 +# DIS-NEXT: 4001d2: 02 0f move $24, $2 + +# DIS-NEXT: 4001d4: 00 79 8e 07 addiupc $2, 7736 +# DIS-NEXT: 4001d8: 22 ff 00 00 lw $25, 0($2) +# DIS-NEXT: 4001dc: 99 45 jr16 $25 +# DIS-NEXT: 4001de: 02 0f move $24, $2 + +# PLT-SYM: Relocations [ +# PLT-SYM-NEXT: Section (5) .rel.dyn { +# PLT-SYM-NEXT: 0x402018 R_MIPS_COPY D1 0x0 +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: Section (6) .rel.plt { +# PLT-SYM-NEXT: 0x402008 R_MIPS_JUMP_SLOT T3 0x0 +# PLT-SYM-NEXT: 0x40200C R_MIPS_JUMP_SLOT T1 0x0 +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: ] + +# PLT-SYM: DynamicSymbols [ +# PLT-SYM-NEXT: Symbol { +# PLT-SYM-NEXT: Name: @ (0) +# PLT-SYM-NEXT: Value: 0x0 +# PLT-SYM-NEXT: Size: 0 +# PLT-SYM-NEXT: Binding: Local (0x0) +# PLT-SYM-NEXT: Type: None (0x0) +# PLT-SYM-NEXT: Other: 0 +# PLT-SYM-NEXT: Section: Undefined (0x0) +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: Symbol { +# PLT-SYM-NEXT: Name: D1@ (1) +# PLT-SYM-NEXT: Value: 0x402018 +# PLT-SYM-NEXT: Size: 8 +# PLT-SYM-NEXT: Binding: Global (0x1) +# PLT-SYM-NEXT: Type: Object (0x1) +# PLT-SYM-NEXT: Other: 0 +# PLT-SYM-NEXT: Section: .bss (0xD) +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: Symbol { +# PLT-SYM-NEXT: Name: T1@ (4) +# PLT-SYM-NEXT: Value: 0x4001D5 +# PLT-SYM-NEXT: Size: 0 +# PLT-SYM-NEXT: Binding: Global (0x1) +# PLT-SYM-NEXT: Type: Function (0x2) +# PLT-SYM-NEXT: Other: 8 +# PLT-SYM-NEXT: Section: Undefined (0x0) +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: Symbol { +# PLT-SYM-NEXT: Name: T3@ (7) +# PLT-SYM-NEXT: Value: 0x0 +# PLT-SYM-NEXT: Size: 0 +# PLT-SYM-NEXT: Binding: Global (0x1) +# PLT-SYM-NEXT: Type: Function (0x2) +# PLT-SYM-NEXT: Other: 0 +# PLT-SYM-NEXT: Section: Undefined (0x0) +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: ] + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, + EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + Other: [ STO_MIPS_MICROMIPS ] + - Name: T3 + Section: .text + Type: STT_FUNC + Value: 0x8 + Size: 4 + Other: [ STO_MIPS_MICROMIPS ] + - Name: D1 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 8 + +# o.o +--- +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x04 + Symbol: T3 + Type: R_MICROMIPS_26_S1 + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: T1 + Type: R_MICROMIPS_HI16 + - Offset: 0x00 + Symbol: T1 + Type: R_MICROMIPS_LO16 + - Offset: 0x04 + Symbol: D1 + Type: R_MICROMIPS_HI16 + - Offset: 0x04 + Symbol: D1 + Type: R_MICROMIPS_LO16 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 8 + Other: [ STO_MIPS_MICROMIPS ] + - Name: T1 + Type: STT_FUNC + - Name: T3 + Type: STT_FUNC + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 8 + - Name: D1 + Type: STT_OBJECT +... diff --git a/test/elf/Mips/rel-dynamic-01.test b/test/elf/Mips/rel-dynamic-01.test new file mode 100644 index 0000000000000..ca4619668bd20 --- /dev/null +++ b/test/elf/Mips/rel-dynamic-01.test @@ -0,0 +1,237 @@ +# REQUIRES: mips + +# Conditions: +# a) Linking a non-shared executable file. +# b) Relocations' targets are symbols defined in the shared object. +# Check: +# a) Emitting R_MIPS_REL32, R_MIPS_COPY, R_MIPS_JUMP_SLOT relocations. +# b) PLT entries creation. +# c) STO_MIPS_PLT flag in the dynamic symbol table for symbols require +# a pointer equality. +# +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o.o %t.so +# RUN: llvm-objdump -disassemble %t.exe | FileCheck -check-prefix=PLT %s +# RUN: llvm-readobj -dt -r %t.exe | FileCheck -check-prefix=PLT-SYM %s + +# PLT: Disassembly of section .plt: +# PLT-NEXT: .plt: +# PLT-NEXT: 4001f0: 40 00 1c 3c lui $gp, 64 +# PLT-NEXT: 4001f4: 00 20 99 8f lw $25, 8192($gp) +# PLT-NEXT: 4001f8: 00 20 9c 27 addiu $gp, $gp, 8192 +# PLT-NEXT: 4001fc: 23 c0 1c 03 subu $24, $24, $gp +# PLT-NEXT: 400200: 21 78 e0 03 move $15, $ra +# PLT-NEXT: 400204: 82 c0 18 00 srl $24, $24, 2 +# PLT-NEXT: 400208: 09 f8 20 03 jalr $25 +# PLT-NEXT: 40020c: fe ff 18 27 addiu $24, $24, -2 +# +# PLT-NEXT: 400210: 40 00 0f 3c lui $15, 64 +# PLT-NEXT: 400214: 08 20 f9 8d lw $25, 8200($15) +# PLT-NEXT: 400218: 08 00 20 03 jr $25 +# PLT-NEXT: 40021c: 08 20 f8 25 addiu $24, $15, 8200 +# +# PLT-NEXT: 400220: 40 00 0f 3c lui $15, 64 +# PLT-NEXT: 400224: 0c 20 f9 8d lw $25, 8204($15) +# PLT-NEXT: 400228: 08 00 20 03 jr $25 +# PLT-NEXT: 40022c: 0c 20 f8 25 addiu $24, $15, 8204 + +# PLT-SYM: Relocations [ +# PLT-SYM-NEXT: Section (5) .rel.dyn { +# PLT-SYM-NEXT: 0x402014 R_MIPS_REL32 T2 0x0 +# PLT-SYM-NEXT: 0x402014 R_MIPS_REL32 D2 0x0 +# PLT-SYM-NEXT: 0x402018 R_MIPS_COPY D1 0x0 +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: Section (6) .rel.plt { +# PLT-SYM-NEXT: 0x402008 R_MIPS_JUMP_SLOT T3 0x0 +# PLT-SYM-NEXT: 0x40200C R_MIPS_JUMP_SLOT T1 0x0 +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: ] + +# PLT-SYM: DynamicSymbols [ +# PLT-SYM-NEXT: Symbol { +# PLT-SYM-NEXT: Name: @ (0) +# PLT-SYM-NEXT: Value: 0x0 +# PLT-SYM-NEXT: Size: 0 +# PLT-SYM-NEXT: Binding: Local (0x0) +# PLT-SYM-NEXT: Type: None (0x0) +# PLT-SYM-NEXT: Other: 0 +# PLT-SYM-NEXT: Section: Undefined (0x0) +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: Symbol { +# PLT-SYM-NEXT: Name: D1@ (1) +# PLT-SYM-NEXT: Value: 0x402018 +# PLT-SYM-NEXT: Size: 4 +# PLT-SYM-NEXT: Binding: Global (0x1) +# PLT-SYM-NEXT: Type: Object (0x1) +# PLT-SYM-NEXT: Other: 0 +# PLT-SYM-NEXT: Section: .bss (0xD) +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: Symbol { +# PLT-SYM-NEXT: Name: T1@ (4) +# PLT-SYM-NEXT: Value: 0x400220 +# PLT-SYM-NEXT: Size: 0 +# PLT-SYM-NEXT: Binding: Global (0x1) +# PLT-SYM-NEXT: Type: Function (0x2) +# PLT-SYM-NEXT: Other: 8 +# PLT-SYM-NEXT: Section: Undefined (0x0) +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: Symbol { +# PLT-SYM-NEXT: Name: T3@ (10) +# PLT-SYM-NEXT: Value: 0x0 +# PLT-SYM-NEXT: Size: 0 +# PLT-SYM-NEXT: Binding: Global (0x1) +# PLT-SYM-NEXT: Type: Function (0x2) +# PLT-SYM-NEXT: Other: 0 +# PLT-SYM-NEXT: Section: Undefined (0x0) +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: Symbol { +# PLT-SYM-NEXT: Name: T2@ (7) +# PLT-SYM-NEXT: Value: 0x0 +# PLT-SYM-NEXT: Size: 0 +# PLT-SYM-NEXT: Binding: Global (0x1) +# PLT-SYM-NEXT: Type: Function (0x2) +# PLT-SYM-NEXT: Other: 0 +# PLT-SYM-NEXT: Section: Undefined (0x0) +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: Symbol { +# PLT-SYM-NEXT: Name: D2@ (13) +# PLT-SYM-NEXT: Value: 0x0 +# PLT-SYM-NEXT: Size: 4 +# PLT-SYM-NEXT: Binding: Global (0x1) +# PLT-SYM-NEXT: Type: Object (0x1) +# PLT-SYM-NEXT: Other: 0 +# PLT-SYM-NEXT: Section: Undefined (0x0) +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: ] + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + - Name: T2 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 4 + - Name: T3 + Section: .text + Type: STT_FUNC + Value: 0x8 + Size: 4 + - Name: D1 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 4 + - Name: D2 + Section: .data + Type: STT_OBJECT + Value: 0x4 + Size: 4 + +# o.o +--- +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x04 + Symbol: T3 + Type: R_MIPS_26 + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_HI16 + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_LO16 + - Offset: 0x04 + Symbol: T2 + Type: R_MIPS_32 + + - Offset: 0x04 + Symbol: D1 + Type: R_MIPS_HI16 + - Offset: 0x04 + Symbol: D1 + Type: R_MIPS_LO16 + - Offset: 0x04 + Symbol: D2 + Type: R_MIPS_32 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 8 + - Name: T1 + Type: STT_FUNC + - Name: T2 + Type: STT_FUNC + - Name: T3 + Type: STT_FUNC + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 8 + - Name: D1 + Type: STT_OBJECT + - Name: D2 + Type: STT_OBJECT +... diff --git a/test/elf/Mips/rel-dynamic-02.test b/test/elf/Mips/rel-dynamic-02.test new file mode 100644 index 0000000000000..4de86383482cb --- /dev/null +++ b/test/elf/Mips/rel-dynamic-02.test @@ -0,0 +1,82 @@ +# Conditions: +# a) Linking a shared library. +# b) Relocations' targets are undefined symbols. +# Check: +# a) Emitting R_MIPS_REL32 relocations for both undefined symbols. +# b) There should be no PLT entries. +# +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: lld -flavor gnu -target mipsel -shared --noinhibit-exec -o %t1-so %t-obj +# RUN: llvm-readobj -dt -r -s %t1-so | FileCheck -check-prefix=PLT-SYM %s + +# PLT-SYM: Sections [ +# PLT-SYM: Section { +# PLT-SYM-NOT: Name: .plt ({{[0-9]+}}) +# +# PLT-SYM: Relocations [ +# PLT-SYM-NEXT: Section (4) .rel.dyn { +# PLT-SYM-NEXT: 0x140 R_MIPS_REL32 T1 0x0 +# PLT-SYM-NEXT: 0x2000 R_MIPS_REL32 T1 0x0 +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: ] +# +# PLT-SYM: Name: T1@ (7) +# PLT-SYM-NEXT: Value: 0x0 +# PLT-SYM-NEXT: Size: 0 +# PLT-SYM-NEXT: Binding: Global (0x1) +# PLT-SYM-NEXT: Type: None (0x0) +# PLT-SYM-NEXT: Other: 0 +# PLT-SYM-NEXT: Section: Undefined (0x0) + +!ELF +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "0000000000000000" + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Content: "0000000000000000" + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_32 + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_32 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + - Name: T1 + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 8 diff --git a/test/elf/Mips/rel-dynamic-03-micro.test b/test/elf/Mips/rel-dynamic-03-micro.test new file mode 100644 index 0000000000000..5de2cdcdc131d --- /dev/null +++ b/test/elf/Mips/rel-dynamic-03-micro.test @@ -0,0 +1,133 @@ +# REQUIRES: mips + +# Conditions: +# a) Linking a non-shared executable file. +# b) Relocations' target is a symbol defined in the shared object. +# c) The target symbol is referenced by both branch (R_MICROMIPS_26_S1) +# and regular (R_MIPS_32) relocations. +# Check: +# a) There should be no R_MIPS_REL32 relocation. +# b) Linker creates a single PLT entry. +# c) STO_MIPS_PLT flag in the dynamic symbol table for symbols require +# a pointer equality. +# +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o.o %t.so +# RUN: llvm-objdump -d -mattr=micromips %t.exe | FileCheck -check-prefix=DIS %s +# RUN: llvm-readobj -dt -r %t.exe | FileCheck -check-prefix=PLT-SYM %s + +# DIS: Disassembly of section .plt: +# DIS-NEXT: .plt: +# DIS-NEXT: 400170: 80 79 a4 07 addiupc $3, 7824 +# DIS-NEXT: 400174: 23 ff 00 00 lw $25, 0($3) +# DIS-NEXT: 400178: 35 05 subu16 $2, $2, $3 +# DIS-NEXT: 40017a: 25 25 srl16 $2, $2, 2 +# DIS-NEXT: 40017c: 02 33 fe ff addiu $24, $2, -2 +# DIS-NEXT: 400180: ff 0d move $15, $ra +# DIS-NEXT: 400182: f9 45 jalrs16 $25 +# DIS-NEXT: 400184: 83 0f move $gp, $3 +# DIS-NEXT: 400186: 00 0c nop + +# DIS-NEXT: 400188: 00 79 a0 07 addiupc $2, 7808 +# DIS-NEXT: 40018c: 22 ff 00 00 lw $25, 0($2) +# DIS-NEXT: 400190: 99 45 jr16 $25 +# DIS-NEXT: 400192: 02 0f move $24, $2 + +# PLT-SYM: Relocations [ +# PLT-SYM-NEXT: Section (5) .rel.plt { +# PLT-SYM-NEXT: 0x402008 R_MIPS_JUMP_SLOT T1 0x0 +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: ] + +# PLT-SYM: Name: T1@ (1) +# PLT-SYM-NEXT: Value: 0x400189 +# PLT-SYM-NEXT: Size: 0 +# PLT-SYM-NEXT: Binding: Global (0x1) +# PLT-SYM-NEXT: Type: Function (0x2) +# PLT-SYM-NEXT: Other: 8 +# PLT-SYM-NEXT: Section: Undefined (0x0) + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, + EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + Other: [ STO_MIPS_MICROMIPS ] + +# o.o +--- +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x04 + Symbol: T1 + Type: R_MICROMIPS_26_S1 + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x04 + Symbol: T1 + Type: R_MIPS_32 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 8 + Other: [ STO_MIPS_MICROMIPS ] + - Name: T1 + Type: STT_FUNC + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 8 +... diff --git a/test/elf/Mips/rel-dynamic-03.test b/test/elf/Mips/rel-dynamic-03.test new file mode 100644 index 0000000000000..cc791b051748a --- /dev/null +++ b/test/elf/Mips/rel-dynamic-03.test @@ -0,0 +1,129 @@ +# REQUIRES: mips + +# Conditions: +# a) Linking a non-shared executable file. +# b) Relocations' target is a symbol defined in the shared object. +# c) The target symbol is referenced by both branch (R_MIPS_26) +# and regular (R_MIPS_32) relocations. +# Check: +# a) There should be no R_MIPS_REL32 relocation. +# b) Linker creates a single PLT entry. +# c) STO_MIPS_PLT flag in the dynamic symbol table for symbols require +# a pointer equality. +# +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o.o %t.so +# RUN: llvm-objdump -disassemble %t.exe | FileCheck -check-prefix=PLT %s +# RUN: llvm-readobj -dt -r %t.exe | FileCheck -check-prefix=PLT-SYM %s + +# PLT: Disassembly of section .plt: +# PLT-NEXT: .plt: +# PLT-NEXT: 400160: 40 00 1c 3c lui $gp, 64 +# PLT-NEXT: 400164: 00 20 99 8f lw $25, 8192($gp) +# PLT-NEXT: 400168: 00 20 9c 27 addiu $gp, $gp, 8192 +# PLT-NEXT: 40016c: 23 c0 1c 03 subu $24, $24, $gp +# PLT-NEXT: 400170: 21 78 e0 03 move $15, $ra +# PLT-NEXT: 400174: 82 c0 18 00 srl $24, $24, 2 +# PLT-NEXT: 400178: 09 f8 20 03 jalr $25 +# PLT-NEXT: 40017c: fe ff 18 27 addiu $24, $24, -2 +# +# PLT-NEXT: 400180: 40 00 0f 3c lui $15, 64 +# PLT-NEXT: 400184: 08 20 f9 8d lw $25, 8200($15) +# PLT-NEXT: 400188: 08 00 20 03 jr $25 +# PLT-NEXT: 40018c: 08 20 f8 25 addiu $24, $15, 8200 +# +# PLT-SYM: Relocations [ +# PLT-SYM-NEXT: Section (5) .rel.plt { +# PLT-SYM-NEXT: 0x402008 R_MIPS_JUMP_SLOT T1 0x0 +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: ] + +# PLT-SYM: Name: T1@ (1) +# PLT-SYM-NEXT: Value: 0x400180 +# PLT-SYM-NEXT: Size: 0 +# PLT-SYM-NEXT: Binding: Global (0x1) +# PLT-SYM-NEXT: Type: Function (0x2) +# PLT-SYM-NEXT: Other: 8 +# PLT-SYM-NEXT: Section: Undefined (0x0) + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + +# o.o +--- +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x04 + Symbol: T1 + Type: R_MIPS_26 + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x04 + Symbol: T1 + Type: R_MIPS_32 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 8 + - Name: T1 + Type: STT_FUNC + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 8 +... diff --git a/test/elf/Mips/rel-dynamic-04-micro.test b/test/elf/Mips/rel-dynamic-04-micro.test new file mode 100644 index 0000000000000..1c58efdf5f70d --- /dev/null +++ b/test/elf/Mips/rel-dynamic-04-micro.test @@ -0,0 +1,211 @@ +# Conditions: +# a) Linking a non-shared executable file. +# b) Relocations' targets are symbols defined in the shared object. +# c) Relocations modify a writable section. +# d) The first symbol is referenced by R_MIPS32 relocation only +# e) The second symbol is referenced by R_MIPS_32 +# and R_MICROMIPS_26_S1 relocations. +# f) The third symbol is referenced by R_MICROMIPS_26_S1 +# and R_MIPS_32 relocations. +# Check: +# a) There should be the only R_MIPS_REL32 relocation. +# b) Linker creates a couple of PLT entry for both symbols referenced +# by the R_MICROMIPS_26_S1 branch relocation. +# c) STO_MIPS_PLT flag in the dynamic symbol table for symbols require +# a pointer equality. +# +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o.o %t.so +# RUN: llvm-readobj -dt -r -s %t.exe | FileCheck -check-prefix=PLT %s + +# PLT: Section { +# PLT: Index: 5 +# PLT-NEXT: Name: .rel.dyn (31) +# PLT-NEXT: Type: SHT_REL (0x9) +# PLT-NEXT: Flags [ (0x2) +# PLT-NEXT: SHF_ALLOC (0x2) +# PLT-NEXT: ] +# PLT-NEXT: Address: 0x4010A0 +# PLT-NEXT: Offset: 0x10A0 +# PLT-NEXT: Size: 8 +# PLT-NEXT: Link: 3 +# PLT-NEXT: Info: 0 +# PLT-NEXT: AddressAlignment: 4 +# PLT-NEXT: EntrySize: 8 +# PLT-NEXT: } +# PLT-NEXT: Section { +# PLT-NEXT: Index: 6 +# PLT-NEXT: Name: .rel.plt (40) +# PLT-NEXT: Type: SHT_REL (0x9) +# PLT-NEXT: Flags [ (0x2) +# PLT-NEXT: SHF_ALLOC (0x2) +# PLT-NEXT: ] +# PLT-NEXT: Address: 0x4010A8 +# PLT-NEXT: Offset: 0x10A8 +# PLT-NEXT: Size: 16 +# PLT-NEXT: Link: 3 +# PLT-NEXT: Info: 0 +# PLT-NEXT: AddressAlignment: 4 +# PLT-NEXT: EntrySize: 8 +# PLT-NEXT: } +# PLT-NEXT: Section { +# PLT-NEXT: Index: 7 +# PLT-NEXT: Name: .plt (49) +# PLT-NEXT: Type: SHT_PROGBITS (0x1) +# PLT-NEXT: Flags [ (0x6) +# PLT-NEXT: SHF_ALLOC (0x2) +# PLT-NEXT: SHF_EXECINSTR (0x4) +# PLT-NEXT: ] +# PLT-NEXT: Address: 0x4010C0 +# PLT-NEXT: Offset: 0x10C0 +# PLT-NEXT: Size: 48 +# PLT-NEXT: Link: 0 +# PLT-NEXT: Info: 0 +# PLT-NEXT: AddressAlignment: 16 +# PLT-NEXT: EntrySize: 0 +# PLT-NEXT: } + +# PLT: Relocations [ +# PLT-NEXT: Section (5) .rel.dyn { +# PLT-NEXT: 0x400120 R_MIPS_REL32 T1 0x0 +# PLT-NEXT: } +# PLT-NEXT: Section (6) .rel.plt { +# PLT-NEXT: 0x403008 R_MIPS_JUMP_SLOT T2 0x0 +# PLT-NEXT: 0x40300C R_MIPS_JUMP_SLOT T3 0x0 +# PLT-NEXT: } +# PLT-NEXT: ] + +# PLT: DynamicSymbols [ +# PLT-NEXT: Symbol { +# PLT-NEXT: Name: @ (0) +# PLT-NEXT: Value: 0x0 +# PLT-NEXT: Size: 0 +# PLT-NEXT: Binding: Local (0x0) +# PLT-NEXT: Type: None (0x0) +# PLT-NEXT: Other: 0 +# PLT-NEXT: Section: Undefined (0x0) +# PLT-NEXT: } +# PLT-NEXT: Symbol { +# PLT-NEXT: Name: T2@ (4) +# PLT-NEXT: Value: 0x4010D9 +# PLT-NEXT: Size: 0 +# PLT-NEXT: Binding: Global (0x1) +# PLT-NEXT: Type: Function (0x2) +# PLT-NEXT: Other: 8 +# PLT-NEXT: Section: Undefined (0x0) +# PLT-NEXT: } +# PLT-NEXT: Symbol { +# PLT-NEXT: Name: T3@ (7) +# PLT-NEXT: Value: 0x4010E5 +# PLT-NEXT: Size: 0 +# PLT-NEXT: Binding: Global (0x1) +# PLT-NEXT: Type: Function (0x2) +# PLT-NEXT: Other: 8 +# PLT-NEXT: Section: Undefined (0x0) +# PLT-NEXT: } +# PLT-NEXT: Symbol { +# PLT-NEXT: Name: T1@ (1) +# PLT-NEXT: Value: 0x0 +# PLT-NEXT: Size: 0 +# PLT-NEXT: Binding: Global (0x1) +# PLT-NEXT: Type: Function (0x2) +# PLT-NEXT: Other: 0 +# PLT-NEXT: Section: Undefined (0x0) +# PLT-NEXT: } +# PLT-NEXT: ] + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + - Name: T2 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 4 + - Name: T3 + Section: .text + Type: STT_FUNC + Value: 0x8 + Size: 4 + +# o.o +--- +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_CPIC, EF_MIPS_ABI_O32, + EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x14 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_EXECINSTR, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + # There is no branch relocation for T1. + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_32 + # The R_MIPS_32 relocation for T2 might produce R_MIPS_REL32 ... + - Offset: 0x04 + Symbol: T2 + Type: R_MIPS_32 + # ... but R_MICROMIPS_26_S1 creates PLT entry + # and makes R_MIPS_REL32 redundant. + - Offset: 0x08 + Symbol: T2 + Type: R_MICROMIPS_26_S1 + # Create PLT entry for T3 symbol. + - Offset: 0x0c + Symbol: T3 + Type: R_MICROMIPS_26_S1 + # Take in account existing PLT entry and do not create R_MIPS_REL32. + - Offset: 0x10 + Symbol: T3 + Type: R_MIPS_32 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 0x14 + Other: [ STO_MIPS_MICROMIPS ] + - Name: T1 + Type: STT_FUNC + - Name: T2 + Type: STT_FUNC + - Name: T3 + Type: STT_FUNC +... diff --git a/test/elf/Mips/rel-dynamic-04.test b/test/elf/Mips/rel-dynamic-04.test new file mode 100644 index 0000000000000..5b5bcce01cd83 --- /dev/null +++ b/test/elf/Mips/rel-dynamic-04.test @@ -0,0 +1,206 @@ +# Conditions: +# a) Linking a non-shared executable file. +# b) Relocations' targets are symbols defined in the shared object. +# c) Relocations modify a writable section. +# d) The first symbol is referenced by R_MIPS32 relocation only +# e) The second symbol is referenced by R_MIPS_32 and R_MIPS26 relocations. +# f) The third symbol is referenced by R_MIPS26 and R_MIPS_32 relocations. +# Check: +# a) There should be the only R_MIPS_REL32 relocation. +# b) Linker creates a couple of PLT entry for both symbols referenced +# by the R_MIPS_26 branch relocation. +# c) STO_MIPS_PLT flag in the dynamic symbol table for symbols require +# a pointer equality. +# +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o.o %t.so +# RUN: llvm-readobj -dt -r -s %t.exe | FileCheck -check-prefix=PLT %s + +# PLT: Section { +# PLT: Index: 5 +# PLT-NEXT: Name: .rel.dyn (31) +# PLT-NEXT: Type: SHT_REL (0x9) +# PLT-NEXT: Flags [ (0x2) +# PLT-NEXT: SHF_ALLOC (0x2) +# PLT-NEXT: ] +# PLT-NEXT: Address: 0x40109C +# PLT-NEXT: Offset: 0x109C +# PLT-NEXT: Size: 8 +# PLT-NEXT: Link: 3 +# PLT-NEXT: Info: 0 +# PLT-NEXT: AddressAlignment: 4 +# PLT-NEXT: EntrySize: 8 +# PLT-NEXT: } +# PLT-NEXT: Section { +# PLT-NEXT: Index: 6 +# PLT-NEXT: Name: .rel.plt (40) +# PLT-NEXT: Type: SHT_REL (0x9) +# PLT-NEXT: Flags [ (0x2) +# PLT-NEXT: SHF_ALLOC (0x2) +# PLT-NEXT: ] +# PLT-NEXT: Address: 0x4010A4 +# PLT-NEXT: Offset: 0x10A4 +# PLT-NEXT: Size: 16 +# PLT-NEXT: Link: 3 +# PLT-NEXT: Info: 0 +# PLT-NEXT: AddressAlignment: 4 +# PLT-NEXT: EntrySize: 8 +# PLT-NEXT: } +# PLT-NEXT: Section { +# PLT-NEXT: Index: 7 +# PLT-NEXT: Name: .plt (49) +# PLT-NEXT: Type: SHT_PROGBITS (0x1) +# PLT-NEXT: Flags [ (0x6) +# PLT-NEXT: SHF_ALLOC (0x2) +# PLT-NEXT: SHF_EXECINSTR (0x4) +# PLT-NEXT: ] +# PLT-NEXT: Address: 0x4010C0 +# PLT-NEXT: Offset: 0x10C0 +# PLT-NEXT: Size: 64 +# PLT-NEXT: Link: 0 +# PLT-NEXT: Info: 0 +# PLT-NEXT: AddressAlignment: 16 +# PLT-NEXT: EntrySize: 0 +# PLT-NEXT: } + +# PLT: Relocations [ +# PLT-NEXT: Section (5) .rel.dyn { +# PLT-NEXT: 0x400120 R_MIPS_REL32 T1 0x0 +# PLT-NEXT: } +# PLT-NEXT: Section (6) .rel.plt { +# PLT-NEXT: 0x403008 R_MIPS_JUMP_SLOT T2 0x0 +# PLT-NEXT: 0x40300C R_MIPS_JUMP_SLOT T3 0x0 +# PLT-NEXT: } +# PLT-NEXT: ] + +# PLT: DynamicSymbols [ +# PLT-NEXT: Symbol { +# PLT-NEXT: Name: @ (0) +# PLT-NEXT: Value: 0x0 +# PLT-NEXT: Size: 0 +# PLT-NEXT: Binding: Local (0x0) +# PLT-NEXT: Type: None (0x0) +# PLT-NEXT: Other: 0 +# PLT-NEXT: Section: Undefined (0x0) +# PLT-NEXT: } +# PLT-NEXT: Symbol { +# PLT-NEXT: Name: T2@ (4) +# PLT-NEXT: Value: 0x4010E0 +# PLT-NEXT: Size: 0 +# PLT-NEXT: Binding: Global (0x1) +# PLT-NEXT: Type: Function (0x2) +# PLT-NEXT: Other: 8 +# PLT-NEXT: Section: Undefined (0x0) +# PLT-NEXT: } +# PLT-NEXT: Symbol { +# PLT-NEXT: Name: T3@ (7) +# PLT-NEXT: Value: 0x4010F0 +# PLT-NEXT: Size: 0 +# PLT-NEXT: Binding: Global (0x1) +# PLT-NEXT: Type: Function (0x2) +# PLT-NEXT: Other: 8 +# PLT-NEXT: Section: Undefined (0x0) +# PLT-NEXT: } +# PLT-NEXT: Symbol { +# PLT-NEXT: Name: T1@ (1) +# PLT-NEXT: Value: 0x0 +# PLT-NEXT: Size: 0 +# PLT-NEXT: Binding: Global (0x1) +# PLT-NEXT: Type: Function (0x2) +# PLT-NEXT: Other: 0 +# PLT-NEXT: Section: Undefined (0x0) +# PLT-NEXT: } +# PLT-NEXT: ] + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + - Name: T2 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 4 + - Name: T3 + Section: .text + Type: STT_FUNC + Value: 0x8 + Size: 4 + +# o.o +--- +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_EXECINSTR, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + # There is no branch relocation for T1. + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_32 + # The R_MIPS_32 relocation for T2 might produce R_MIPS_REL32 ... + - Offset: 0x00 + Symbol: T2 + Type: R_MIPS_32 + # ... but R_MIPS_26 creates PLT entry and makes R_MIPS_REL32 redundant. + - Offset: 0x04 + Symbol: T2 + Type: R_MIPS_26 + # Create PLT entry for T3 symbol. + - Offset: 0x00 + Symbol: T3 + Type: R_MIPS_26 + # Take in account existing PLT entry and do not create R_MIPS_REL32. + - Offset: 0x04 + Symbol: T3 + Type: R_MIPS_32 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 8 + - Name: T1 + Type: STT_FUNC + - Name: T2 + Type: STT_FUNC + - Name: T3 + Type: STT_FUNC +... diff --git a/test/elf/Mips/rel-dynamic-05-micro.test b/test/elf/Mips/rel-dynamic-05-micro.test new file mode 100644 index 0000000000000..d1c87076b596c --- /dev/null +++ b/test/elf/Mips/rel-dynamic-05-micro.test @@ -0,0 +1,192 @@ +# Conditions: +# a) Linking a non-shared executable file. +# b) Relocations' targets are symbols defined in the shared object. +# c) Relocations modify a read-only section. +# d) The first symbol is referenced by R_MIPS32 relocation only +# e) The second symbol is referenced by R_MIPS_32 +# and R_MICROMIPS_26_S1 relocations. +# f) The third symbol is referenced by R_MICROMIPS_26_S1 +# and R_MIPS_32 relocations. +# Check: +# a) There should be no R_MIPS_REL32 relocations. +# b) Linker creates PLT entries for all three relocations. +# c) STO_MIPS_PLT flag in the dynamic symbol table for symbols require +# a pointer equality. +# +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o.o %t.so +# RUN: llvm-readobj -dt -r -s %t.exe | FileCheck -check-prefix=PLT %s + +# PLT: Section { +# PLT: Index: 5 +# PLT-NEXT: Name: .rel.plt (31) +# PLT-NEXT: Type: SHT_REL (0x9) +# PLT-NEXT: Flags [ (0x2) +# PLT-NEXT: SHF_ALLOC (0x2) +# PLT-NEXT: ] +# PLT-NEXT: Address: 0x400194 +# PLT-NEXT: Offset: 0x194 +# PLT-NEXT: Size: 24 +# PLT-NEXT: Link: 3 +# PLT-NEXT: Info: 0 +# PLT-NEXT: AddressAlignment: 4 +# PLT-NEXT: EntrySize: 8 +# PLT-NEXT: } +# PLT-NEXT: Section { +# PLT-NEXT: Index: 6 +# PLT-NEXT: Name: .plt (40) +# PLT-NEXT: Type: SHT_PROGBITS (0x1) +# PLT-NEXT: Flags [ (0x6) +# PLT-NEXT: SHF_ALLOC (0x2) +# PLT-NEXT: SHF_EXECINSTR (0x4) +# PLT-NEXT: ] +# PLT-NEXT: Address: 0x4001B0 +# PLT-NEXT: Offset: 0x1B0 +# PLT-NEXT: Size: 60 +# PLT-NEXT: Link: 0 +# PLT-NEXT: Info: 0 +# PLT-NEXT: AddressAlignment: 16 +# PLT-NEXT: EntrySize: 0 +# PLT-NEXT: } + +# PLT: Relocations [ +# PLT-NEXT: Section (5) .rel.plt { +# PLT-NEXT: 0x402008 R_MIPS_JUMP_SLOT T1 0x0 +# PLT-NEXT: 0x40200C R_MIPS_JUMP_SLOT T2 0x0 +# PLT-NEXT: 0x402010 R_MIPS_JUMP_SLOT T3 0x0 +# PLT-NEXT: } +# PLT-NEXT: ] + +# PLT: DynamicSymbols [ +# PLT-NEXT: Symbol { +# PLT-NEXT: Name: @ (0) +# PLT-NEXT: Value: 0x0 +# PLT-NEXT: Size: 0 +# PLT-NEXT: Binding: Local (0x0) +# PLT-NEXT: Type: None (0x0) +# PLT-NEXT: Other: 0 +# PLT-NEXT: Section: Undefined (0x0) +# PLT-NEXT: } +# PLT-NEXT: Symbol { +# PLT-NEXT: Name: T1@ (1) +# PLT-NEXT: Value: 0x4001C9 +# PLT-NEXT: Size: 0 +# PLT-NEXT: Binding: Global (0x1) +# PLT-NEXT: Type: Function (0x2) +# PLT-NEXT: Other: 8 +# PLT-NEXT: Section: Undefined (0x0) +# PLT-NEXT: } +# PLT-NEXT: Symbol { +# PLT-NEXT: Name: T2@ (4) +# PLT-NEXT: Value: 0x4001D5 +# PLT-NEXT: Size: 0 +# PLT-NEXT: Binding: Global (0x1) +# PLT-NEXT: Type: Function (0x2) +# PLT-NEXT: Other: 8 +# PLT-NEXT: Section: Undefined (0x0) +# PLT-NEXT: } +# PLT-NEXT: Symbol { +# PLT-NEXT: Name: T3@ (7) +# PLT-NEXT: Value: 0x4001E1 +# PLT-NEXT: Size: 0 +# PLT-NEXT: Binding: Global (0x1) +# PLT-NEXT: Type: Function (0x2) +# PLT-NEXT: Other: 8 +# PLT-NEXT: Section: Undefined (0x0) +# PLT-NEXT: } +# PLT-NEXT: ] + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + - Name: T2 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 4 + - Name: T3 + Section: .text + Type: STT_FUNC + Value: 0x8 + Size: 4 + +# o.o +--- +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_CPIC, EF_MIPS_ABI_O32, + EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x14 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + # There is no branch relocation for T1. + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_32 + # The R_MIPS_32 relocation for T2 might produce R_MIPS_REL32 ... + - Offset: 0x04 + Symbol: T2 + Type: R_MIPS_32 + # ... but R_MICROMIPS_26_S1 creates PLT entry and makes R_MIPS_REL32 redundant. + - Offset: 0x08 + Symbol: T2 + Type: R_MICROMIPS_26_S1 + # Create PLT entry for T3 symbol. + - Offset: 0x0C + Symbol: T3 + Type: R_MICROMIPS_26_S1 + # Take in account existing PLT entry and do not create R_MIPS_REL32. + - Offset: 0x10 + Symbol: T3 + Type: R_MIPS_32 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 0x14 + Other: [ STO_MIPS_MICROMIPS ] + - Name: T1 + Type: STT_FUNC + - Name: T2 + Type: STT_FUNC + - Name: T3 + Type: STT_FUNC +... diff --git a/test/elf/Mips/rel-dynamic-05.test b/test/elf/Mips/rel-dynamic-05.test new file mode 100644 index 0000000000000..3bfbd3f6efdbc --- /dev/null +++ b/test/elf/Mips/rel-dynamic-05.test @@ -0,0 +1,188 @@ +# Conditions: +# a) Linking a non-shared executable file. +# b) Relocations' targets are symbols defined in the shared object. +# c) Relocations modify a read-only section. +# d) The first symbol is referenced by R_MIPS32 relocation only +# e) The second symbol is referenced by R_MIPS_32 and R_MIPS26 relocations. +# f) The third symbol is referenced by R_MIPS26 and R_MIPS_32 relocations. +# Check: +# a) There should be no R_MIPS_REL32 relocations. +# b) Linker creates PLT entries for all three relocations. +# c) STO_MIPS_PLT flag in the dynamic symbol table for symbols require +# a pointer equality. +# +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o.o %t.so +# RUN: llvm-readobj -dt -r -s %t.exe | FileCheck -check-prefix=PLT %s + +# PLT: Section { +# PLT: Index: 5 +# PLT-NEXT: Name: .rel.plt (31) +# PLT-NEXT: Type: SHT_REL (0x9) +# PLT-NEXT: Flags [ (0x2) +# PLT-NEXT: SHF_ALLOC (0x2) +# PLT-NEXT: ] +# PLT-NEXT: Address: 0x400190 +# PLT-NEXT: Offset: 0x190 +# PLT-NEXT: Size: 24 +# PLT-NEXT: Link: 3 +# PLT-NEXT: Info: 0 +# PLT-NEXT: AddressAlignment: 4 +# PLT-NEXT: EntrySize: 8 +# PLT-NEXT: } +# PLT-NEXT: Section { +# PLT-NEXT: Index: 6 +# PLT-NEXT: Name: .plt (40) +# PLT-NEXT: Type: SHT_PROGBITS (0x1) +# PLT-NEXT: Flags [ (0x6) +# PLT-NEXT: SHF_ALLOC (0x2) +# PLT-NEXT: SHF_EXECINSTR (0x4) +# PLT-NEXT: ] +# PLT-NEXT: Address: 0x4001B0 +# PLT-NEXT: Offset: 0x1B0 +# PLT-NEXT: Size: 80 +# PLT-NEXT: Link: 0 +# PLT-NEXT: Info: 0 +# PLT-NEXT: AddressAlignment: 16 +# PLT-NEXT: EntrySize: 0 +# PLT-NEXT: } + +# PLT: Relocations [ +# PLT-NEXT: Section (5) .rel.plt { +# PLT-NEXT: 0x402008 R_MIPS_JUMP_SLOT T1 0x0 +# PLT-NEXT: 0x40200C R_MIPS_JUMP_SLOT T2 0x0 +# PLT-NEXT: 0x402010 R_MIPS_JUMP_SLOT T3 0x0 +# PLT-NEXT: } +# PLT-NEXT: ] + +# PLT: DynamicSymbols [ +# PLT-NEXT: Symbol { +# PLT-NEXT: Name: @ (0) +# PLT-NEXT: Value: 0x0 +# PLT-NEXT: Size: 0 +# PLT-NEXT: Binding: Local (0x0) +# PLT-NEXT: Type: None (0x0) +# PLT-NEXT: Other: 0 +# PLT-NEXT: Section: Undefined (0x0) +# PLT-NEXT: } +# PLT-NEXT: Symbol { +# PLT-NEXT: Name: T1@ (1) +# PLT-NEXT: Value: 0x4001D0 +# PLT-NEXT: Size: 0 +# PLT-NEXT: Binding: Global (0x1) +# PLT-NEXT: Type: Function (0x2) +# PLT-NEXT: Other: 8 +# PLT-NEXT: Section: Undefined (0x0) +# PLT-NEXT: } +# PLT-NEXT: Symbol { +# PLT-NEXT: Name: T2@ (4) +# PLT-NEXT: Value: 0x4001E0 +# PLT-NEXT: Size: 0 +# PLT-NEXT: Binding: Global (0x1) +# PLT-NEXT: Type: Function (0x2) +# PLT-NEXT: Other: 8 +# PLT-NEXT: Section: Undefined (0x0) +# PLT-NEXT: } +# PLT-NEXT: Symbol { +# PLT-NEXT: Name: T3@ (7) +# PLT-NEXT: Value: 0x4001F0 +# PLT-NEXT: Size: 0 +# PLT-NEXT: Binding: Global (0x1) +# PLT-NEXT: Type: Function (0x2) +# PLT-NEXT: Other: 8 +# PLT-NEXT: Section: Undefined (0x0) +# PLT-NEXT: } +# PLT-NEXT: ] + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + - Name: T2 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 4 + - Name: T3 + Section: .text + Type: STT_FUNC + Value: 0x8 + Size: 4 + +# o.o +--- +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + # There is no branch relocation for T1. + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_32 + # The R_MIPS_32 relocation for T2 might produce R_MIPS_REL32 ... + - Offset: 0x00 + Symbol: T2 + Type: R_MIPS_32 + # ... but R_MIPS_26 creates PLT entry and makes R_MIPS_REL32 redundant. + - Offset: 0x04 + Symbol: T2 + Type: R_MIPS_26 + # Create PLT entry for T3 symbol. + - Offset: 0x00 + Symbol: T3 + Type: R_MIPS_26 + # Take in account existing PLT entry and do not create R_MIPS_REL32. + - Offset: 0x04 + Symbol: T3 + Type: R_MIPS_32 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 8 + - Name: T1 + Type: STT_FUNC + - Name: T2 + Type: STT_FUNC + - Name: T3 + Type: STT_FUNC +... diff --git a/test/elf/Mips/rel-dynamic-06-64.test b/test/elf/Mips/rel-dynamic-06-64.test new file mode 100644 index 0000000000000..c153eb22af4c5 --- /dev/null +++ b/test/elf/Mips/rel-dynamic-06-64.test @@ -0,0 +1,101 @@ +# Conditions: +# a) Linking a shared library. +# b) The first relocation modifies a regular .text section. +# c) The second relocation modifies a .pdr section without SHF_ALLOC flag. +# Check: +# a) There should be no PLT entries. +# b) Linker creates a single R_MIPS_REL32 relocation. +# +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mips64el -shared -o %t.so %t.o +# RUN: llvm-readobj -dt -r -s %t.so | FileCheck -check-prefix=CHECK %s + +# CHECK: Sections [ +# CHECK: Section { +# CHECK-NOT: Name: .plt ({{[0-9]+}}) + +# CHECK: Relocations [ +# CHECK-NEXT: Section (4) .rel.dyn { +# CHECK-NEXT: 0x170 R_MIPS_REL32/R_MIPS_64/R_MIPS_NONE T0 0x0 +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# CHECK: DynamicSymbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: @ (0) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local (0x0) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: T1@ (4) +# CHECK-NEXT: Value: 0x174 +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text (0x5) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: T0@ (1) +# CHECK-NEXT: Value: 0x170 +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text (0x5) +# CHECK-NEXT: } +# CHECK-NEXT: ] + +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ARCH_64] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 8 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_RELA + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0 + Symbol: T0 + Type: R_MIPS_64 + +- Name: .pdr + Type: SHT_PROGBITS + Size: 8 + AddressAlign: 16 + +- Name: .rel.pdr + Type: SHT_RELA + Info: .pdr + AddressAlign: 4 + Relocations: + - Offset: 0 + Symbol: T1 + Type: R_MIPS_64 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 4 + Size: 4 diff --git a/test/elf/Mips/rel-dynamic-06.test b/test/elf/Mips/rel-dynamic-06.test new file mode 100644 index 0000000000000..433eb5fadf59f --- /dev/null +++ b/test/elf/Mips/rel-dynamic-06.test @@ -0,0 +1,103 @@ +# Conditions: +# a) Linking a shared library. +# b) The first relocation modifies a regular .text section. +# c) The second relocation modifies a .pdr section without SHF_ALLOC flag. +# Check: +# a) There should be no PLT entries. +# b) Linker creates a single R_MIPS_REL32 relocation. +# +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: lld -flavor gnu -target mipsel -shared -o %t1-so %t-obj +# RUN: llvm-readobj -dt -r -s %t1-so | FileCheck -check-prefix=PLT1 %s + +# PLT1-SYM: Sections [ +# PLT1-SYM: Section { +# PLT1-SYM-NOT: Name: .plt ({{[0-9]+}}) + +# PLT1: Relocations [ +# PLT1-NEXT: Section (4) .rel.dyn { +# PLT1-NEXT: 0x100 R_MIPS_REL32 T0 0x0 +# PLT1-NEXT: } +# PLT1-NEXT: ] + +# PLT1: DynamicSymbols [ +# PLT1-NEXT: Symbol { +# PLT1-NEXT: Name: @ (0) +# PLT1-NEXT: Value: 0x0 +# PLT1-NEXT: Size: 0 +# PLT1-NEXT: Binding: Local (0x0) +# PLT1-NEXT: Type: None (0x0) +# PLT1-NEXT: Other: 0 +# PLT1-NEXT: Section: Undefined (0x0) +# PLT1-NEXT: } +# PLT1-NEXT: Symbol { +# PLT1-NEXT: Name: T1@ (4) +# PLT1-NEXT: Value: 0x104 +# PLT1-NEXT: Size: 4 +# PLT1-NEXT: Binding: Global (0x1) +# PLT1-NEXT: Type: Function (0x2) +# PLT1-NEXT: Other: 0 +# PLT1-NEXT: Section: .text (0x5) +# PLT1-NEXT: } +# PLT1-NEXT: Symbol { +# PLT1-NEXT: Name: T0@ (1) +# PLT1-NEXT: Value: 0x100 +# PLT1-NEXT: Size: 4 +# PLT1-NEXT: Binding: Global (0x1) +# PLT1-NEXT: Type: Function (0x2) +# PLT1-NEXT: Other: 0 +# PLT1-NEXT: Section: .text (0x5) +# PLT1-NEXT: } +# PLT1-NEXT: ] + +!ELF +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "0000000000000000" + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: T0 + Type: R_MIPS_32 + +- Name: .pdr + Type: SHT_PROGBITS + Content: "0000000000000000" + AddressAlign: 16 + Flags: [] + +- Name: .rel.pdr + Type: SHT_REL + Info: .pdr + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_32 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 4 diff --git a/test/elf/Mips/rel-dynamic-07-64.test b/test/elf/Mips/rel-dynamic-07-64.test new file mode 100644 index 0000000000000..f7a1c44258234 --- /dev/null +++ b/test/elf/Mips/rel-dynamic-07-64.test @@ -0,0 +1,261 @@ +# Conditions: +# a) Linking a shared library. +# b) There ars multiple R_MIPS_64 relocations with various targets. +# Check: +# a) Emitting of R_MIPS_REL32 relocations. +# +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mips64el -shared -o %t1.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mips64el -shared --noinhibit-exec \ +# RUN: -o %t2.so %t-o.o %t1.so +# RUN: llvm-readobj -dt -r -sections %t2.so | FileCheck %s + +# CHECK: Sections [ +# CHECK: Section { +# CHECK-NOT: Name: .plt ({{[0-9]+}}) + +# CHECK: Relocations [ +# CHECK-NEXT: Section (4) .rel.dyn { +# CHECK-NEXT: 0x2000 R_MIPS_REL32/R_MIPS_64/R_MIPS_NONE T0 0x0 +# CHECK-NEXT: 0x2000 R_MIPS_REL32/R_MIPS_64/R_MIPS_NONE T4 0x0 +# CHECK-NEXT: 0x2000 R_MIPS_REL32/R_MIPS_64/R_MIPS_NONE D2 0x0 +# CHECK-NEXT: 0x2004 R_MIPS_REL32/R_MIPS_64/R_MIPS_NONE T1 0x0 +# CHECK-NEXT: 0x2008 R_MIPS_REL32/R_MIPS_64/R_MIPS_NONE T2 0x0 +# CHECK-NEXT: 0x2004 R_MIPS_REL32/R_MIPS_64/R_MIPS_NONE D0 0x0 +# CHECK-NEXT: 0x2008 R_MIPS_REL32/R_MIPS_64/R_MIPS_NONE D1 0x0 +# CHECK-NEXT: 0x2004 R_MIPS_REL32/R_MIPS_64/R_MIPS_NONE D4 0x0 +# CHECK-NEXT: 0x2008 R_MIPS_REL32/R_MIPS_64/R_MIPS_NONE U1 0x0 +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# CHECK: DynamicSymbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: @ (0) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local (0x0) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: T0@ (1) +# CHECK-NEXT: Value: 0x324 +# CHECK-NEXT: Size: 8 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text (0x5) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: T4@ (7) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: D2@ (25) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Object (0x1) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: T1@ (16) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: T2@ (19) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: D0@ (4) +# CHECK-NEXT: Value: 0x2004 +# CHECK-NEXT: Size: 8 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Object (0x1) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .data (0x8) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: D1@ (22) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Object (0x1) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: D4@ (10) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: U1@ (13) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# so.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ARCH_64] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + - Name: T2 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 4 + - Name: D1 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 4 + - Name: D2 + Section: .data + Type: STT_OBJECT + Value: 0x4 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ARCH_64] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.data + Type: SHT_RELA + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x00 # T0 is a defined function + Symbol: T0 + Type: R_MIPS_64 + - Offset: 0x04 # T1 is a function from shared lib + Symbol: T1 + Type: R_MIPS_64 + - Offset: 0x08 # T2 has unknown type and defined in shared lib + Symbol: T2 + Type: R_MIPS_64 + - Offset: 0x00 # T4 is an undefined function + Symbol: T4 + Type: R_MIPS_64 + - Offset: 0x04 # D0 is a defined data object + Symbol: D0 + Type: R_MIPS_64 + - Offset: 0x08 # D1 is a data object from shared lib + Symbol: D1 + Type: R_MIPS_64 + - Offset: 0x00 # D2 has unknown type and defined in shared lib + Symbol: D2 + Type: R_MIPS_64 + - Offset: 0x04 # D4 is an undefined data object + Symbol: D4 + Type: R_MIPS_64 + - Offset: 0x08 # U1 is undefined and has unknown type + Symbol: U1 + Type: R_MIPS_64 + +Symbols: + Local: + - Name: LT0 + Section: .text + Type: STT_FUNC + Value: 0 + Size: 4 + - Name: LD0 + Section: .data + Type: STT_OBJECT + Value: 0 + Size: 4 + + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 8 + - Name: T1 + Type: STT_FUNC + - Name: T2 + - Name: T4 + Type: STT_FUNC + + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0x4 + Size: 8 + - Name: D1 + Type: STT_OBJECT + - Name: D2 + - Name: D4 + Type: STT_OBJECT + - Name: U1 +... diff --git a/test/elf/Mips/rel-dynamic-07.test b/test/elf/Mips/rel-dynamic-07.test new file mode 100644 index 0000000000000..fc163b9a12f7e --- /dev/null +++ b/test/elf/Mips/rel-dynamic-07.test @@ -0,0 +1,276 @@ +# Conditions: +# a) Linking a shared library. +# b) There ars multiple R_MIPS_32 relocations with various targets. +# Check: +# a) Emitting of R_MIPS_REL32 relocations. +# b) There should be no R_MIPS_REL32 relocations for the _gp_disp symbol. +# +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t1.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -shared --noinhibit-exec \ +# RUN: -o %t2.so %t-o.o %t1.so +# RUN: llvm-readobj -dt -r -sections %t2.so | FileCheck %s + +# CHECK: Sections [ +# CHECK: Section { +# CHECK-NOT: Name: .plt ({{[0-9]+}}) + +# CHECK: Relocations [ +# CHECK-NEXT: Section (4) .rel.dyn { +# CHECK-NEXT: 0x2000 R_MIPS_REL32 T0 0x0 +# CHECK-NEXT: 0x2000 R_MIPS_REL32 T4 0x0 +# CHECK-NEXT: 0x2000 R_MIPS_REL32 D2 0x0 +# CHECK-NEXT: 0x2004 R_MIPS_REL32 T1 0x0 +# CHECK-NEXT: 0x2008 R_MIPS_REL32 T2 0x0 +# CHECK-NEXT: 0x2004 R_MIPS_REL32 D0 0x0 +# CHECK-NEXT: 0x2008 R_MIPS_REL32 D1 0x0 +# CHECK-NEXT: 0x2004 R_MIPS_REL32 D4 0x0 +# CHECK-NEXT: 0x2008 R_MIPS_REL32 U1 0x0 +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# CHECK: DynamicSymbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: @ (0) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local (0x0) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: T0@ (1) +# CHECK-NEXT: Value: 0x214 +# CHECK-NEXT: Size: 8 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .text (0x5) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: T4@ (7) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: D2@ (25) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Object (0x1) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: T1@ (16) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: T2@ (19) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: D0@ (4) +# CHECK-NEXT: Value: 0x2004 +# CHECK-NEXT: Size: 8 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Object (0x1) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .data (0x8) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: D1@ (22) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Object (0x1) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: D4@ (10) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: U1@ (13) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + - Name: T2 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 4 + - Name: D1 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 4 + - Name: D2 + Section: .data + Type: STT_OBJECT + Value: 0x4 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: _gp_disp + Type: R_MIPS_HI16 + - Offset: 0x00 + Symbol: _gp_disp + Type: R_MIPS_LO16 + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x00 # T0 is a defined function + Symbol: T0 + Type: R_MIPS_32 + - Offset: 0x04 # T1 is a function from shared lib + Symbol: T1 + Type: R_MIPS_32 + - Offset: 0x08 # T2 has unknown type and defined in shared lib + Symbol: T2 + Type: R_MIPS_32 + - Offset: 0x00 # T4 is an undefined function + Symbol: T4 + Type: R_MIPS_32 + - Offset: 0x04 # D0 is a defined data object + Symbol: D0 + Type: R_MIPS_32 + - Offset: 0x08 # D1 is a data object from shared lib + Symbol: D1 + Type: R_MIPS_32 + - Offset: 0x00 # D2 has unknown type and defined in shared lib + Symbol: D2 + Type: R_MIPS_32 + - Offset: 0x04 # D4 is an undefined data object + Symbol: D4 + Type: R_MIPS_32 + - Offset: 0x08 # U1 is undefined and has unknown type + Symbol: U1 + Type: R_MIPS_32 + +Symbols: + Local: + - Name: LT0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + - Name: LD0 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 4 + + Global: + - Name: _gp_disp + Type: STT_OBJECT + + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 8 + - Name: T1 + Type: STT_FUNC + - Name: T2 + - Name: T4 + Type: STT_FUNC + + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0x4 + Size: 8 + - Name: D1 + Type: STT_OBJECT + - Name: D2 + - Name: D4 + Type: STT_OBJECT + - Name: U1 diff --git a/test/elf/Mips/rel-dynamic-08-64.test b/test/elf/Mips/rel-dynamic-08-64.test new file mode 100644 index 0000000000000..d845d7407c1c4 --- /dev/null +++ b/test/elf/Mips/rel-dynamic-08-64.test @@ -0,0 +1,233 @@ +# Conditions: +# a) Linking a non-shared executable file. +# b) There ars multiple R_MIPS_64/R_MIPS_HI16/R_MIPS_LO16 relocations +# with various targets. +# Check: +# a) Emitting of R_MIPS_REL32 relocations. +# +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mips64el -shared -o %t.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mips64el -e T0 --noinhibit-exec \ +# RUN: -o %t.exe %t-o.o %t.so +# RUN: llvm-readobj -dt -r -sections %t.exe | FileCheck %s + +# CHECK: Sections [ +# CHECK: Section { +# CHECK-NOT: Name: .plt ({{[0-9]+}}) + +# CHECK: Relocations [ +# CHECK-NEXT: Section (5) .rel.dyn { +# CHECK-NEXT: 0x120002000 R_MIPS_REL32/R_MIPS_64/R_MIPS_NONE D2 0x0 +# CHECK-NEXT: 0x120002004 R_MIPS_REL32/R_MIPS_64/R_MIPS_NONE T1 0x0 +# CHECK-NEXT: 0x120002008 R_MIPS_REL32/R_MIPS_64/R_MIPS_NONE T2 0x0 +# CHECK-NEXT: 0x120002008 R_MIPS_REL32/R_MIPS_64/R_MIPS_NONE D1 0x0 +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# CHECK: DynamicSymbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: @ (0) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local (0x0) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: D2@ (10) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Object (0x1) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: T1@ (1) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: T2@ (4) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: D1@ (7) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Object (0x1) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# so.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ARCH_64] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + - Name: T2 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 4 + - Name: D1 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 4 + - Name: D2 + Section: .data + Type: STT_OBJECT + Value: 0x4 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ARCH_64] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_RELA + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: _gp_disp + Type: R_MIPS_HI16 + - Offset: 0x00 + Symbol: _gp_disp + Type: R_MIPS_LO16 + +- Name: .rel.data + Type: SHT_RELA + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x00 # LT0 is a locally defined function + Symbol: LT0 + Type: R_MIPS_64 + - Offset: 0x00 # LD0 is a locally defined data object + Symbol: LD0 + Type: R_MIPS_64 + - Offset: 0x00 # T0 is a defined function + Symbol: T0 + Type: R_MIPS_64 + - Offset: 0x04 # T1 is a function from shared lib + Symbol: T1 + Type: R_MIPS_64 + - Offset: 0x08 # T2 has unknown type and defined in shared lib + Symbol: T2 + Type: R_MIPS_64 + - Offset: 0x00 # T4 is an undefined function + Symbol: T4 + Type: R_MIPS_64 + - Offset: 0x04 # D0 is a defined data object + Symbol: D0 + Type: R_MIPS_64 + - Offset: 0x08 # D1 is a data object from shared lib + Symbol: D1 + Type: R_MIPS_64 + - Offset: 0x00 # D2 has unknown type and defined in shared lib + Symbol: D2 + Type: R_MIPS_64 + - Offset: 0x04 # D4 is an undefined data object + Symbol: D4 + Type: R_MIPS_64 + - Offset: 0x08 # U1 is undefined and has unknown type + Symbol: U1 + Type: R_MIPS_64 + +Symbols: + Local: + - Name: LT0 + Section: .text + Type: STT_FUNC + Value: 0 + Size: 4 + - Name: LD0 + Section: .data + Type: STT_OBJECT + Value: 0 + Size: 4 + + Global: + - Name: _gp_disp + Type: STT_OBJECT + + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 8 + - Name: T1 + Type: STT_FUNC + - Name: T2 + - Name: T4 + Type: STT_FUNC + + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0x4 + Size: 8 + - Name: D1 + Type: STT_OBJECT + - Name: D2 + - Name: D4 + Type: STT_OBJECT + - Name: U1 +... diff --git a/test/elf/Mips/rel-dynamic-08-micro.test b/test/elf/Mips/rel-dynamic-08-micro.test new file mode 100644 index 0000000000000..de0038956086e --- /dev/null +++ b/test/elf/Mips/rel-dynamic-08-micro.test @@ -0,0 +1,236 @@ +# Conditions: +# a) Linking a non-shared executable file. +# b) There ars multiple R_MIPS_32/R_MICROMIPS_HI16/R_MICROMIPS_LO16 +# relocations with various targets. +# Check: +# a) Emitting of R_MIPS_REL32 relocations. +# +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e T0 --noinhibit-exec \ +# RUN: -o %t.exe %t-o.o %t.so +# RUN: llvm-readobj -dt -r -sections %t.exe | FileCheck %s + +# CHECK: Sections [ +# CHECK: Section { +# CHECK-NOT: Name: .plt ({{[0-9]+}}) + +# CHECK: Relocations [ +# CHECK-NEXT: Section (5) .rel.dyn { +# CHECK-NEXT: 0x402000 R_MIPS_REL32 D2 0x0 +# CHECK-NEXT: 0x402004 R_MIPS_REL32 T1 0x0 +# CHECK-NEXT: 0x402008 R_MIPS_REL32 T2 0x0 +# CHECK-NEXT: 0x402008 R_MIPS_REL32 D1 0x0 +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# CHECK: DynamicSymbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: @ (0) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local (0x0) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: D2@ (10) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Object (0x1) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: T1@ (1) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: T2@ (4) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: D1@ (7) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Object (0x1) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + - Name: T2 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 4 + - Name: D1 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 4 + - Name: D2 + Section: .data + Type: STT_OBJECT + Value: 0x4 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_CPIC, EF_MIPS_ABI_O32, + EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: _gp_disp + Type: R_MICROMIPS_HI16 + - Offset: 0x00 + Symbol: _gp_disp + Type: R_MICROMIPS_LO16 + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x00 # LT0 is a locally defined function + Symbol: LT0 + Type: R_MIPS_32 + - Offset: 0x00 # LD0 is a locally defined data object + Symbol: LD0 + Type: R_MIPS_32 + - Offset: 0x00 # T0 is a defined function + Symbol: T0 + Type: R_MIPS_32 + - Offset: 0x04 # T1 is a function from shared lib + Symbol: T1 + Type: R_MIPS_32 + - Offset: 0x08 # T2 has unknown type and defined in shared lib + Symbol: T2 + Type: R_MIPS_32 + - Offset: 0x00 # T4 is an undefined function + Symbol: T4 + Type: R_MIPS_32 + - Offset: 0x04 # D0 is a defined data object + Symbol: D0 + Type: R_MIPS_32 + - Offset: 0x08 # D1 is a data object from shared lib + Symbol: D1 + Type: R_MIPS_32 + - Offset: 0x00 # D2 has unknown type and defined in shared lib + Symbol: D2 + Type: R_MIPS_32 + - Offset: 0x04 # D4 is an undefined data object + Symbol: D4 + Type: R_MIPS_32 + - Offset: 0x08 # U1 is undefined and has unknown type + Symbol: U1 + Type: R_MIPS_32 + +Symbols: + Local: + - Name: LT0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + Other: [ STO_MIPS_MICROMIPS ] + - Name: LD0 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 4 + + Global: + - Name: _gp_disp + Type: STT_OBJECT + + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 8 + Other: [ STO_MIPS_MICROMIPS ] + - Name: T1 + Type: STT_FUNC + - Name: T2 + - Name: T4 + Type: STT_FUNC + + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0x4 + Size: 8 + - Name: D1 + Type: STT_OBJECT + - Name: D2 + - Name: D4 + Type: STT_OBJECT + - Name: U1 +... diff --git a/test/elf/Mips/rel-dynamic-08.test b/test/elf/Mips/rel-dynamic-08.test new file mode 100644 index 0000000000000..62f4dc278b051 --- /dev/null +++ b/test/elf/Mips/rel-dynamic-08.test @@ -0,0 +1,233 @@ +# Conditions: +# a) Linking a non-shared executable file. +# b) There ars multiple R_MIPS_32/R_MIPS_HI16/R_MIPS_LO16 relocations +# with various targets. +# Check: +# a) Emitting of R_MIPS_REL32 relocations. +# +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e T0 --noinhibit-exec \ +# RUN: -o %t.exe %t-o.o %t.so +# RUN: llvm-readobj -dt -r -sections %t.exe | FileCheck %s + +# CHECK: Sections [ +# CHECK: Section { +# CHECK-NOT: Name: .plt ({{[0-9]+}}) + +# CHECK: Relocations [ +# CHECK-NEXT: Section (5) .rel.dyn { +# CHECK-NEXT: 0x402000 R_MIPS_REL32 D2 0x0 +# CHECK-NEXT: 0x402004 R_MIPS_REL32 T1 0x0 +# CHECK-NEXT: 0x402008 R_MIPS_REL32 T2 0x0 +# CHECK-NEXT: 0x402008 R_MIPS_REL32 D1 0x0 +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# CHECK: DynamicSymbols [ +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: @ (0) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Local (0x0) +# CHECK-NEXT: Type: None (0x0) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: D2@ (10) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Object (0x1) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: T1@ (1) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: T2@ (4) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 0 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Function (0x2) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: D1@ (7) +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Object (0x1) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: Undefined (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + - Name: T2 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 4 + - Name: D1 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 4 + - Name: D2 + Section: .data + Type: STT_OBJECT + Value: 0x4 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: _gp_disp + Type: R_MIPS_HI16 + - Offset: 0x00 + Symbol: _gp_disp + Type: R_MIPS_LO16 + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x00 # LT0 is a locally defined function + Symbol: LT0 + Type: R_MIPS_32 + - Offset: 0x00 # LD0 is a locally defined data object + Symbol: LD0 + Type: R_MIPS_32 + - Offset: 0x00 # T0 is a defined function + Symbol: T0 + Type: R_MIPS_32 + - Offset: 0x04 # T1 is a function from shared lib + Symbol: T1 + Type: R_MIPS_32 + - Offset: 0x08 # T2 has unknown type and defined in shared lib + Symbol: T2 + Type: R_MIPS_32 + - Offset: 0x00 # T4 is an undefined function + Symbol: T4 + Type: R_MIPS_32 + - Offset: 0x04 # D0 is a defined data object + Symbol: D0 + Type: R_MIPS_32 + - Offset: 0x08 # D1 is a data object from shared lib + Symbol: D1 + Type: R_MIPS_32 + - Offset: 0x00 # D2 has unknown type and defined in shared lib + Symbol: D2 + Type: R_MIPS_32 + - Offset: 0x04 # D4 is an undefined data object + Symbol: D4 + Type: R_MIPS_32 + - Offset: 0x08 # U1 is undefined and has unknown type + Symbol: U1 + Type: R_MIPS_32 + +Symbols: + Local: + - Name: LT0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + - Name: LD0 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 4 + + Global: + - Name: _gp_disp + Type: STT_OBJECT + + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 8 + - Name: T1 + Type: STT_FUNC + - Name: T2 + - Name: T4 + Type: STT_FUNC + + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0x4 + Size: 8 + - Name: D1 + Type: STT_OBJECT + - Name: D2 + - Name: D4 + Type: STT_OBJECT + - Name: U1 +... diff --git a/test/elf/Mips/rel-dynamic-09-micro.test b/test/elf/Mips/rel-dynamic-09-micro.test new file mode 100644 index 0000000000000..07ffce9eb0744 --- /dev/null +++ b/test/elf/Mips/rel-dynamic-09-micro.test @@ -0,0 +1,109 @@ +# Conditions: +# a) Linking a non-shared executable file. +# b) Relocations' targets are undefined symbols. +# Check: +# a) There should be no dynamic relocations. +# b) There should be no PLT entries. +# +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: lld -flavor gnu -target mipsel --noinhibit-exec -e T0 -o %t2-exe %t-obj +# RUN: llvm-readobj -dt -r -s %t2-exe | FileCheck -check-prefix=PLT-SYM %s + +# PLT-SYM: Sections [ +# PLT-SYM: Section { +# PLT-SYM-NOT: Name: .plt ({{[0-9]+}}) + +# PLT-SYM: Relocations [ +# PLT-SYM-NEXT: ] + +# PLT-SYM: DynamicSymbols [ +# PLT-SYM-NEXT: Symbol { +# PLT-SYM-NEXT: Name: @ (0) +# PLT-SYM-NEXT: Value: 0x0 +# PLT-SYM-NEXT: Size: 0 +# PLT-SYM-NEXT: Binding: Local (0x0) +# PLT-SYM-NEXT: Type: None (0x0) +# PLT-SYM-NEXT: Other: 0 +# PLT-SYM-NEXT: Section: Undefined (0x0) +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: ] + +!ELF +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_CPIC, EF_MIPS_ABI_O32, + EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "0000000000000000" + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Content: "0000000000000000" + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x04 + Symbol: T3 + Type: R_MICROMIPS_26_S1 + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: T1 + Type: R_MICROMIPS_HI16 + - Offset: 0x00 + Symbol: T1 + Type: R_MICROMIPS_LO16 + - Offset: 0x04 + Symbol: T2 + Type: R_MIPS_32 + + - Offset: 0x04 + Symbol: D1 + Type: R_MICROMIPS_HI16 + - Offset: 0x04 + Symbol: D1 + Type: R_MICROMIPS_LO16 + - Offset: 0x04 + Symbol: D2 + Type: R_MIPS_32 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 8 + Other: [ STO_MIPS_MICROMIPS ] + - Name: T1 + Type: STT_FUNC + - Name: T2 + Type: STT_FUNC + - Name: T3 + Type: STT_FUNC + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 8 + - Name: D1 + Type: STT_OBJECT + - Name: D2 + Type: STT_OBJECT diff --git a/test/elf/Mips/rel-dynamic-09.test b/test/elf/Mips/rel-dynamic-09.test new file mode 100644 index 0000000000000..18eeb9dd33d8c --- /dev/null +++ b/test/elf/Mips/rel-dynamic-09.test @@ -0,0 +1,107 @@ +# Conditions: +# a) Linking a non-shared executable file. +# b) Relocations' targets are undefined symbols. +# Check: +# a) There should be no dynamic relocations. +# b) There should be no PLT entries. +# +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: lld -flavor gnu -target mipsel --noinhibit-exec -e T0 -o %t2-exe %t-obj +# RUN: llvm-readobj -dt -r -s %t2-exe | FileCheck -check-prefix=PLT-SYM %s + +# PLT-SYM: Sections [ +# PLT-SYM: Section { +# PLT-SYM-NOT: Name: .plt ({{[0-9]+}}) + +# PLT-SYM: Relocations [ +# PLT-SYM-NEXT: ] + +# PLT-SYM: DynamicSymbols [ +# PLT-SYM-NEXT: Symbol { +# PLT-SYM-NEXT: Name: @ (0) +# PLT-SYM-NEXT: Value: 0x0 +# PLT-SYM-NEXT: Size: 0 +# PLT-SYM-NEXT: Binding: Local (0x0) +# PLT-SYM-NEXT: Type: None (0x0) +# PLT-SYM-NEXT: Other: 0 +# PLT-SYM-NEXT: Section: Undefined (0x0) +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: ] + +!ELF +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32 ] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "0000000000000000" + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Content: "0000000000000000" + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x04 + Symbol: T3 + Type: R_MIPS_26 + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_HI16 + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_LO16 + - Offset: 0x04 + Symbol: T2 + Type: R_MIPS_32 + + - Offset: 0x04 + Symbol: D1 + Type: R_MIPS_HI16 + - Offset: 0x04 + Symbol: D1 + Type: R_MIPS_LO16 + - Offset: 0x04 + Symbol: D2 + Type: R_MIPS_32 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 8 + - Name: T1 + Type: STT_FUNC + - Name: T2 + Type: STT_FUNC + - Name: T3 + Type: STT_FUNC + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 8 + - Name: D1 + Type: STT_OBJECT + - Name: D2 + Type: STT_OBJECT diff --git a/test/elf/Mips/rel-dynamic-10-micro.test b/test/elf/Mips/rel-dynamic-10-micro.test new file mode 100644 index 0000000000000..6b3f2af3db322 --- /dev/null +++ b/test/elf/Mips/rel-dynamic-10-micro.test @@ -0,0 +1,166 @@ +# Conditions: +# a) Linking a non-shared executable file. +# b) Relocations' targets are symbols defined in the other object. +# Check: +# a) There should be no dynamic relocations. +# b) There should be no PLT entries. +# +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-o1.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o2.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o1.o %t-o2.o +# RUN: llvm-readobj -dt -r -s %t.exe | FileCheck -check-prefix=PLT-SYM %s + +# PLT-SYM: Sections [ +# PLT-SYM: Section { +# PLT-SYM-NOT: Name: .plt ({{[0-9]+}}) + +# PLT-SYM: Relocations [ +# PLT-SYM-NEXT: ] + +# PLT-SYM: DynamicSymbols [ +# PLT-SYM-NEXT: Symbol { +# PLT-SYM-NEXT: Name: @ (0) +# PLT-SYM-NEXT: Value: 0x0 +# PLT-SYM-NEXT: Size: 0 +# PLT-SYM-NEXT: Binding: Local (0x0) +# PLT-SYM-NEXT: Type: None (0x0) +# PLT-SYM-NEXT: Other: 0 +# PLT-SYM-NEXT: Section: Undefined (0x0) +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: ] + +# o1.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, + EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + Other: [ STO_MIPS_MICROMIPS ] + - Name: T2 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 4 + Other: [ STO_MIPS_MICROMIPS ] + - Name: T3 + Section: .text + Type: STT_FUNC + Value: 0x8 + Size: 4 + Other: [ STO_MIPS_MICROMIPS ] + - Name: D1 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 4 + - Name: D2 + Section: .data + Type: STT_OBJECT + Value: 0x4 + Size: 4 + +# o2.o +--- +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_CPIC, EF_MIPS_ABI_O32, + EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x04 + Symbol: T3 + Type: R_MICROMIPS_26_S1 + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: T1 + Type: R_MICROMIPS_HI16 + - Offset: 0x00 + Symbol: T1 + Type: R_MICROMIPS_LO16 + - Offset: 0x04 + Symbol: T2 + Type: R_MIPS_32 + + - Offset: 0x04 + Symbol: D1 + Type: R_MICROMIPS_HI16 + - Offset: 0x04 + Symbol: D1 + Type: R_MICROMIPS_LO16 + - Offset: 0x04 + Symbol: D2 + Type: R_MIPS_32 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 8 + Other: [ STO_MIPS_MICROMIPS ] + - Name: T1 + Type: STT_FUNC + - Name: T2 + Type: STT_FUNC + - Name: T3 + Type: STT_FUNC + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 8 + - Name: D1 + Type: STT_OBJECT + - Name: D2 + Type: STT_OBJECT +... diff --git a/test/elf/Mips/rel-dynamic-10.test b/test/elf/Mips/rel-dynamic-10.test new file mode 100644 index 0000000000000..4df558167fc79 --- /dev/null +++ b/test/elf/Mips/rel-dynamic-10.test @@ -0,0 +1,160 @@ +# Conditions: +# a) Linking a non-shared executable file. +# b) Relocations' targets are symbols defined in the other object. +# Check: +# a) There should be no dynamic relocations. +# b) There should be no PLT entries. +# +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-o1.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o2.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o1.o %t-o2.o +# RUN: llvm-readobj -dt -r -s %t.exe | FileCheck -check-prefix=PLT-SYM %s + +# PLT-SYM: Sections [ +# PLT-SYM: Section { +# PLT-SYM-NOT: Name: .plt ({{[0-9]+}}) + +# PLT-SYM: Relocations [ +# PLT-SYM-NEXT: ] + +# PLT-SYM: DynamicSymbols [ +# PLT-SYM-NEXT: Symbol { +# PLT-SYM-NEXT: Name: @ (0) +# PLT-SYM-NEXT: Value: 0x0 +# PLT-SYM-NEXT: Size: 0 +# PLT-SYM-NEXT: Binding: Local (0x0) +# PLT-SYM-NEXT: Type: None (0x0) +# PLT-SYM-NEXT: Other: 0 +# PLT-SYM-NEXT: Section: Undefined (0x0) +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: ] + +# o1.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + - Name: T2 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 4 + - Name: T3 + Section: .text + Type: STT_FUNC + Value: 0x8 + Size: 4 + - Name: D1 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 4 + - Name: D2 + Section: .data + Type: STT_OBJECT + Value: 0x4 + Size: 4 + +# o2.o +--- +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x04 + Symbol: T3 + Type: R_MIPS_26 + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_HI16 + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_LO16 + - Offset: 0x04 + Symbol: T2 + Type: R_MIPS_32 + + - Offset: 0x04 + Symbol: D1 + Type: R_MIPS_HI16 + - Offset: 0x04 + Symbol: D1 + Type: R_MIPS_LO16 + - Offset: 0x04 + Symbol: D2 + Type: R_MIPS_32 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 8 + - Name: T1 + Type: STT_FUNC + - Name: T2 + Type: STT_FUNC + - Name: T3 + Type: STT_FUNC + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 8 + - Name: D1 + Type: STT_OBJECT + - Name: D2 + Type: STT_OBJECT +... diff --git a/test/elf/Mips/rel-dynamic-11.test b/test/elf/Mips/rel-dynamic-11.test new file mode 100644 index 0000000000000..20295396cd083 --- /dev/null +++ b/test/elf/Mips/rel-dynamic-11.test @@ -0,0 +1,110 @@ +# Conditions: +# a) Linking a shared library. +# b) Relocations' targets are symbols defined in the other shared object. +# Check: +# a) Emitting R_MIPS_REL32 relocations for both symbols. +# b) There should be no PLT entries. +# +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t1.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t2.so %t-o.o %t1.so +# RUN: llvm-readobj -dt -r -s %t2.so | FileCheck -check-prefix=PLT-SYM %s + +# PLT-SYM: Sections [ +# PLT-SYM: Section { +# PLT-SYM-NOT: Name: .plt ({{[0-9]+}}) +# +# PLT-SYM: Relocations [ +# PLT-SYM-NEXT: Section (4) .rel.dyn { +# PLT-SYM-NEXT: 0x150 R_MIPS_REL32 T1 0x0 +# PLT-SYM-NEXT: 0x2000 R_MIPS_REL32 T1 0x0 +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: ] +# +# PLT-SYM: Name: T1@ (7) +# PLT-SYM-NEXT: Value: 0x0 +# PLT-SYM-NEXT: Size: 0 +# PLT-SYM-NEXT: Binding: Global (0x1) +# PLT-SYM-NEXT: Type: Function (0x2) +# PLT-SYM-NEXT: Other: 0 +# PLT-SYM-NEXT: Section: Undefined (0x0) + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_32 + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_32 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + - Name: T1 + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 8 +... diff --git a/test/elf/Mips/rel-dynamic-12.test b/test/elf/Mips/rel-dynamic-12.test new file mode 100644 index 0000000000000..2a4061ab40359 --- /dev/null +++ b/test/elf/Mips/rel-dynamic-12.test @@ -0,0 +1,213 @@ +# Conditions: +# a) Linking a non-shared executable file. +# b) Relocations' targets are symbols defined in the shared object. +# c) Relocations are R_MIPS_PCHI16 / R_MIPS_PCLO16. +# Check: +# a) Emitting R_MIPS_REL32, R_MIPS_COPY, R_MIPS_JUMP_SLOT relocations. +# b) STO_MIPS_PLT flag in the dynamic symbol table for symbols require +# a pointer equality. +# +# RUN: yaml2obj -format=elf -docnum 1 %s > %t-so.o +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t-o.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o.o %t.so +# RUN: llvm-readobj -dt -r %t.exe | FileCheck -check-prefix=PLT-SYM %s + +# PLT-SYM: Relocations [ +# PLT-SYM-NEXT: Section (5) .rel.dyn { +# PLT-SYM-NEXT: 0x402014 R_MIPS_REL32 T2 0x0 +# PLT-SYM-NEXT: 0x402014 R_MIPS_REL32 D2 0x0 +# PLT-SYM-NEXT: 0x402018 R_MIPS_COPY D1 0x0 +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: Section (6) .rel.plt { +# PLT-SYM-NEXT: 0x402008 R_MIPS_JUMP_SLOT T3 0x0 +# PLT-SYM-NEXT: 0x40200C R_MIPS_JUMP_SLOT T1 0x0 +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: ] + +# PLT-SYM: DynamicSymbols [ +# PLT-SYM-NEXT: Symbol { +# PLT-SYM-NEXT: Name: @ (0) +# PLT-SYM-NEXT: Value: 0x0 +# PLT-SYM-NEXT: Size: 0 +# PLT-SYM-NEXT: Binding: Local (0x0) +# PLT-SYM-NEXT: Type: None (0x0) +# PLT-SYM-NEXT: Other: 0 +# PLT-SYM-NEXT: Section: Undefined (0x0) +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: Symbol { +# PLT-SYM-NEXT: Name: D1@ (1) +# PLT-SYM-NEXT: Value: 0x402018 +# PLT-SYM-NEXT: Size: 4 +# PLT-SYM-NEXT: Binding: Global (0x1) +# PLT-SYM-NEXT: Type: Object (0x1) +# PLT-SYM-NEXT: Other: 0 +# PLT-SYM-NEXT: Section: .bss (0xD) +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: Symbol { +# PLT-SYM-NEXT: Name: T1@ (4) +# PLT-SYM-NEXT: Value: 0x400220 +# PLT-SYM-NEXT: Size: 0 +# PLT-SYM-NEXT: Binding: Global (0x1) +# PLT-SYM-NEXT: Type: Function (0x2) +# PLT-SYM-NEXT: Other: 8 +# PLT-SYM-NEXT: Section: Undefined (0x0) +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: Symbol { +# PLT-SYM-NEXT: Name: T3@ (10) +# PLT-SYM-NEXT: Value: 0x0 +# PLT-SYM-NEXT: Size: 0 +# PLT-SYM-NEXT: Binding: Global (0x1) +# PLT-SYM-NEXT: Type: Function (0x2) +# PLT-SYM-NEXT: Other: 0 +# PLT-SYM-NEXT: Section: Undefined (0x0) +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: Symbol { +# PLT-SYM-NEXT: Name: T2@ (7) +# PLT-SYM-NEXT: Value: 0x0 +# PLT-SYM-NEXT: Size: 0 +# PLT-SYM-NEXT: Binding: Global (0x1) +# PLT-SYM-NEXT: Type: Function (0x2) +# PLT-SYM-NEXT: Other: 0 +# PLT-SYM-NEXT: Section: Undefined (0x0) +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: Symbol { +# PLT-SYM-NEXT: Name: D2@ (13) +# PLT-SYM-NEXT: Value: 0x0 +# PLT-SYM-NEXT: Size: 4 +# PLT-SYM-NEXT: Binding: Global (0x1) +# PLT-SYM-NEXT: Type: Object (0x1) +# PLT-SYM-NEXT: Other: 0 +# PLT-SYM-NEXT: Section: Undefined (0x0) +# PLT-SYM-NEXT: } +# PLT-SYM-NEXT: ] + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R6] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x0C + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +Symbols: + Global: + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 4 + - Name: T2 + Section: .text + Type: STT_FUNC + Value: 0x4 + Size: 4 + - Name: T3 + Section: .text + Type: STT_FUNC + Value: 0x8 + Size: 4 + - Name: D1 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 4 + - Name: D2 + Section: .data + Type: STT_OBJECT + Value: 0x4 + Size: 4 + +# o.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R6] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_EXECINSTR, SHF_ALLOC] + +- Name: .data + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_WRITE, SHF_ALLOC] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0x04 + Symbol: T3 + Type: R_MIPS_26 + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_PCHI16 + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_PCLO16 + - Offset: 0x04 + Symbol: T2 + Type: R_MIPS_32 + + - Offset: 0x04 + Symbol: D1 + Type: R_MIPS_PCHI16 + - Offset: 0x04 + Symbol: D1 + Type: R_MIPS_PCLO16 + - Offset: 0x04 + Symbol: D2 + Type: R_MIPS_32 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 8 + - Name: T1 + Type: STT_FUNC + - Name: T2 + Type: STT_FUNC + - Name: T3 + Type: STT_FUNC + - Name: D0 + Section: .data + Type: STT_OBJECT + Value: 0x0 + Size: 8 + - Name: D1 + Type: STT_OBJECT + - Name: D2 + Type: STT_OBJECT +... diff --git a/test/elf/Mips/rel-gprel16.test b/test/elf/Mips/rel-gprel16.test new file mode 100644 index 0000000000000..dc188ea6825a7 --- /dev/null +++ b/test/elf/Mips/rel-gprel16.test @@ -0,0 +1,104 @@ +# Check R_MIPS_GPREL16 relocation handling. +# +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: lld -flavor gnu -target mipsel -e G1 -shared -o %t.so %t-obj +# RUN: llvm-readobj -symbols %t.so | FileCheck -check-prefix=SYM %s +# RUN: llvm-objdump -s %t.so | FileCheck -check-prefix=SEC %s + +# SYM: Name: L1 (1) +# SYM-NEXT: Value: 0xCC +# SYM-NEXT: Size: 4 +# SYM-NEXT: Binding: Local (0x0) +# SYM-NEXT: Type: Function (0x2) +# SYM-NEXT: Other: 0 +# SYM-NEXT: Section: .text (0x4) + +# SYM: Name: G1 (4) +# SYM-NEXT: Value: 0xD0 +# SYM-NEXT: Size: 4 +# SYM-NEXT: Binding: Global (0x1) +# SYM-NEXT: Type: Function (0x2) +# SYM-NEXT: Other: 0 +# SYM-NEXT: Section: .text (0x4) + +# SYM: Name: _gp (34) +# SYM-NEXT: Value: 0x8FF0 +# SYM-NEXT: Size: 0 +# SYM-NEXT: Binding: Global (0x1) +# SYM-NEXT: Type: Object (0x1) +# SYM-NEXT: Other: 0 +# SYM-NEXT: Section: Absolute (0xFFF1) + +# 0x160db == 0xffff (addend) + 0x00cc (L1) + 0x01f000 (GP0) - 0x8ff0 (_gp) +# SEC: Contents of section .rodata: +# SEC-NEXT: 00d4 db600008 00000000 00000000 00000000 .`.............. + +!ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ] +Sections: +- Type: SHT_PROGBITS + Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x08 + +- Type: SHT_REL + Name: .rel.text + Type: SHT_REL + Link: .symtab + Info: .text + AddressAlign: 0x04 + Relocations: + - Offset: 0 + Symbol: .rodata + Type: R_MIPS_GOT16 + - Offset: 4 + Symbol: .rodata + Type: R_MIPS_LO16 + +- Type: SHT_PROGBITS + Name: .rodata + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x04 + Content: ffff0008000000000000000000000000 + +- Type: SHT_REL + Name: .rel.rodata + Type: SHT_REL + Link: .symtab + Info: .rodata + AddressAlign: 0x04 + Relocations: + - Offset: 0 + Symbol: L1 + Type: R_MIPS_GPREL16 + +- Type: SHT_MIPS_REGINFO + Name: .reginfo + Type: SHT_MIPS_REGINFO + Flags: [ SHF_ALLOC ] + AddressAlign: 0x01 + Content: 000000000000000000000000000000000000000000f00100 + +Symbols: + Local: + - Name: L1 + Section: .text + Value: 0x00 + Size: 0x04 + - Name: .rodata + Type: STT_SECTION + Section: .rodata + Global: + - Name: G1 + Section: .text + Value: 0x04 + Size: 0x04 diff --git a/test/elf/Mips/rel-gprel32-64.test b/test/elf/Mips/rel-gprel32-64.test new file mode 100644 index 0000000000000..723c8e1ee7386 --- /dev/null +++ b/test/elf/Mips/rel-gprel32-64.test @@ -0,0 +1,70 @@ +# Check R_MIPS_GPREL32/R_MIPS_64 relocations handling. + +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mips64el -e T0 -o %t.exe %t.o +# RUN: llvm-objdump -s %t.exe | FileCheck %s + +# CHECK: Contents of section .text: +# CHECK-NEXT: 1200001a0 c871ffff ffffffff c871ffff c871ffff .q.......q...q.. +# CHECK-NEXT: 1200001b0 c871ffff ffffffff 00000000 00000000 .q.............. + +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ARCH_64] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 16 + Size: 32 + + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 8 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: LT1 + Type: R_MIPS_GPREL16 + Type2: R_MIPS_64 + Type3: R_MIPS_NONE + - Offset: 0x08 + Symbol: LT1 + Type: R_MIPS_GPREL16 + Type2: R_MIPS_64 + Type3: R_MIPS_NONE + - Offset: 0x0C + Symbol: LT1 + Type: R_MIPS_GPREL32 + Type2: R_MIPS_64 + Type3: R_MIPS_NONE + - Offset: 0x10 + Symbol: LT1 + Type: R_MIPS_GPREL32 + Type2: R_MIPS_64 + Type3: R_MIPS_NONE + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + + Global: + - Name: LT1 + Type: STT_FUNC + Section: .text + Value: 0x18 + Size: 0x8 + - Name: T0 + Type: STT_FUNC + Section: .text + Value: 0x0 + Size: 0x18 +... diff --git a/test/elf/Mips/rel-gprel32.test b/test/elf/Mips/rel-gprel32.test new file mode 100644 index 0000000000000..73ae6f161979c --- /dev/null +++ b/test/elf/Mips/rel-gprel32.test @@ -0,0 +1,84 @@ +# Check R_MIPS_GPREL32 relocation handling. +# +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: lld -flavor gnu -target mipsel -o %t-exe %t-obj +# RUN: llvm-readobj -symbols %t-exe | FileCheck -check-prefix=SYM %s +# RUN: llvm-objdump -s %t-exe | FileCheck -check-prefix=SEC %s + +# SYM: Name: $L1 (1) +# SYM-NEXT: Value: 0x400108 +# SYM-NEXT: Size: 4 +# SYM-NEXT: Binding: Local (0x0) +# SYM-NEXT: Type: Function (0x2) +# SYM-NEXT: Other: 0 +# SYM-NEXT: Section: .text (0x5) +# +# SYM: Name: _gp (212) +# SYM-NEXT: Value: 0x408FF0 +# SYM-NEXT: Size: 0 +# SYM-NEXT: Binding: Global (0x1) +# SYM-NEXT: Type: Object (0x1) +# SYM-NEXT: Other: 0 +# SYM-NEXT: Section: Absolute (0xFFF1) + +# 0x08FF711B == 0x8000001 (addend) + 0x400108 ($L1) + +# 0x1000002 (GP0) - 0x408FF0 (_gp) +# SEC: Contents of section .rodata: +# SEC-NEXT: 400118 1b71ff08 00000000 00000000 00000000 .q.............. + +!ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ] +Sections: +- Type: SHT_PROGBITS + Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Content: 00000000000000000000000000000000 + +- Type: SHT_PROGBITS + Name: .rodata + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x04 + Content: 01000008000000000000000000000000 + +- Type: SHT_REL + Name: .rel.rodata + Type: SHT_REL + Link: .symtab + Info: .rodata + AddressAlign: 0x04 + Relocations: + - Offset: 0 + Symbol: $L1 + Type: R_MIPS_GPREL32 + +- Type: SHT_MIPS_REGINFO + Name: .reginfo + Type: SHT_MIPS_REGINFO + Flags: [ SHF_ALLOC ] + AddressAlign: 0x01 + Content: 000000000000000000000000000000000000000002000001 + +Symbols: + Local: + - Name: $L1 + Section: .text + Value: 0x00 + - Name: .rodata + Type: STT_SECTION + Section: .rodata + Global: + - Name: __start + Section: .text + Type: STT_FUNC + Value: 0x04 + Size: 12 + - Name: _gp_disp diff --git a/test/elf/Mips/rel-pc-hilo.test b/test/elf/Mips/rel-pc-hilo.test new file mode 100644 index 0000000000000..89cd2b121797a --- /dev/null +++ b/test/elf/Mips/rel-pc-hilo.test @@ -0,0 +1,70 @@ +# Check handling of R_MIPS_PCHI16 / R_MIPS_PCLO16 relocations. + +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t.o +# RUN: llvm-objdump -s -t %t.exe | FileCheck %s + +# CHECK: Contents of section .text: +# CHECK-NEXT: 400110 01000000 02000000 03000000 00000000 +# ^ +# A = 0x10000 - 1 == 0xffff +# V = (T1 + 0xffff - T0) >> 16 => +# V => 0x1000b >> 16 = 1 +# ^ +# A = 0x20000 - 1 == 0x1ffff +# V = (T1 + 0x1ffff - T0 - 4) >> 16 => +# V => 0x20007 >> 16 = 2 +# ^ +# A = 0xffff == -1 +# V = T1 - 1 - T0 - 8 = 3 + +# CHECK: SYMBOL TABLE: +# CHECK: 00400110 g F .text 0000000c T0 +# CHECK: 0040011c g F .text 00000004 T1 + +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R6] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "0100000002000000ffff000000000000" +# ^ T0 +# ^ A := 0x1 == 0x10000 +# ^ A := 0x2 == 0x20000 +# ^ A := 0xffff == -1 +# ^ T1 + AddressAlign: 16 + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0 + Symbol: T1 + Type: R_MIPS_PCHI16 + - Offset: 4 + Symbol: T1 + Type: R_MIPS_PCHI16 + - Offset: 8 + Symbol: T1 + Type: R_MIPS_PCLO16 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0 + Size: 12 + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 12 + Size: 4 diff --git a/test/elf/Mips/rel-pc18-s3.test b/test/elf/Mips/rel-pc18-s3.test new file mode 100644 index 0000000000000..5d5d5c7ce658e --- /dev/null +++ b/test/elf/Mips/rel-pc18-s3.test @@ -0,0 +1,54 @@ +# Check handling of R_MIPS_PC18_S3 relocation. + +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t.o +# RUN: llvm-objdump -s -t %t.exe | FileCheck %s + +# CHECK: Contents of section .text: +# CHECK-NEXT: 400110 00000000 01000000 00000000 00000000 +# ^ V +# A = -1 << 3 = -8 => +# V = (T1 - 8 - (T0|7)^7) >> 3 => +# V => 8 >> 3 = 1 + +# CHECK: SYMBOL TABLE: +# CHECK: 00400110 g F .text 00000010 T0 +# CHECK: 00400120 g F .text 00000004 T1 + +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R6] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "00000000ffff0300000000000000000000000000" +# ^ T1 +# ^ T0 ^ A := 0x3ffff == -1 + AddressAlign: 16 + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 4 + Symbol: T1 + Type: R_MIPS_PC18_S3 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0 + Size: 16 + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 16 + Size: 4 diff --git a/test/elf/Mips/rel-pc19-s2.test b/test/elf/Mips/rel-pc19-s2.test new file mode 100644 index 0000000000000..479965df814fa --- /dev/null +++ b/test/elf/Mips/rel-pc19-s2.test @@ -0,0 +1,54 @@ +# Check handling of R_MIPS_PC19_S2 relocation. + +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t.o +# RUN: llvm-objdump -s -t %t.exe | FileCheck %s + +# CHECK: Contents of section .text: +# CHECK-NEXT: 400110 01000000 00000000 00000000 +# ^ V +# A = -1 << 2 = -4 => +# V = (T1 - 4 - T0) >> 2 => +# V => 4 >> 2 = 1 + +# CHECK: SYMBOL TABLE: +# CHECK: 00400110 g F .text 00000008 T0 +# CHECK: 00400118 g F .text 00000004 T1 + +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R6] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "ffff07000000000000000000" +# ^ T1 +# ^ T0 A := 0x7ffff == -1 + AddressAlign: 16 + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0 + Symbol: T1 + Type: R_MIPS_PC19_S2 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0 + Size: 8 + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 8 + Size: 4 diff --git a/test/elf/Mips/rel-pc21-s2.test b/test/elf/Mips/rel-pc21-s2.test new file mode 100644 index 0000000000000..44d840e94c32c --- /dev/null +++ b/test/elf/Mips/rel-pc21-s2.test @@ -0,0 +1,54 @@ +# Check handling of R_MIPS_PC21_S2 relocation. + +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t.o +# RUN: llvm-objdump -s -t %t.exe | FileCheck %s + +# CHECK: Contents of section .text: +# CHECK-NEXT: 400110 01000000 00000000 00000000 +# ^ V +# A = -1 << 2 = -4 => +# V = (T1 - 4 - T0) >> 2 => +# V => 4 >> 2 = 1 + +# CHECK: SYMBOL TABLE: +# CHECK: 00400110 g F .text 00000008 T0 +# CHECK: 00400118 g F .text 00000004 T1 + +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R6] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "ffff1f000000000000000000" +# ^ T1 +# ^ T0 A := 0x1fffff + AddressAlign: 16 + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0 + Symbol: T1 + Type: R_MIPS_PC21_S2 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0 + Size: 8 + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 8 + Size: 4 diff --git a/test/elf/Mips/rel-pc26-s2.test b/test/elf/Mips/rel-pc26-s2.test new file mode 100644 index 0000000000000..abd05040f0c34 --- /dev/null +++ b/test/elf/Mips/rel-pc26-s2.test @@ -0,0 +1,54 @@ +# Check handling of R_MIPS_PC26_S2 relocation. + +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t.o +# RUN: llvm-objdump -s -t %t.exe | FileCheck %s + +# CHECK: Contents of section .text: +# CHECK-NEXT: 400110 01000000 00000000 00000000 +# ^ V +# A = -1 << 2 = -4 => +# V = (T1 - 4 - T0) >> 2 => +# V => 4 >> 2 = 1 + +# CHECK: SYMBOL TABLE: +# CHECK: 00400110 g F .text 00000008 T0 +# CHECK: 00400118 g F .text 00000004 T1 + +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R6] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "ffffff030000000000000000" +# ^ T1 +# ^ T0 A := 0x3ffffff == -1 + AddressAlign: 16 + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0 + Symbol: T1 + Type: R_MIPS_PC26_S2 + +Symbols: + Global: + - Name: T0 + Section: .text + Type: STT_FUNC + Value: 0 + Size: 8 + - Name: T1 + Section: .text + Type: STT_FUNC + Value: 8 + Size: 4 diff --git a/test/elf/Mips/rel-pc32.test b/test/elf/Mips/rel-pc32.test new file mode 100644 index 0000000000000..e448e8afc30e5 --- /dev/null +++ b/test/elf/Mips/rel-pc32.test @@ -0,0 +1,59 @@ +# Check handling of R_MIPS_PC32 relocation. +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: lld -flavor gnu -target mipsel -o %t-exe %t-obj +# RUN: llvm-objdump -s -t %t-exe | FileCheck %s + +# CHECK: Contents of section .data: +# CHECK-NEXT: 402000 00000000 05000080 fdffff7f ............ +# ^^ data2 + 0x80000001 - data1 +# ^^ data1 + 0x80000001 - data2 +# CHECK: SYMBOL TABLE: +# CHECK: 00402004 g .data 00000004 data1 +# CHECK: 00402008 g .data 00000004 data2 + +!ELF +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "00000000" + AddressAlign: 16 + Flags: [SHF_ALLOC] +- Name: .data + Type: SHT_PROGBITS + Content: "000000000100008001000080" + AddressAlign: 16 + Flags: [SHF_ALLOC, SHF_WRITE] + +- Name: .rel.data + Type: SHT_REL + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x4 + Symbol: data2 + Type: R_MIPS_PC32 + - Offset: 0x8 + Symbol: data1 + Type: R_MIPS_PC32 + +Symbols: + Global: + - Name: __start + Section: .text + Value: 0x0 + Size: 4 + - Name: data1 + Section: .data + Value: 0x4 + Size: 4 + - Name: data2 + Section: .data + Value: 0x8 + Size: 4 diff --git a/test/elf/Mips/rel-pc7-10-16-23.test b/test/elf/Mips/rel-pc7-10-16-23.test new file mode 100644 index 0000000000000..c38b9eed2a5ee --- /dev/null +++ b/test/elf/Mips/rel-pc7-10-16-23.test @@ -0,0 +1,86 @@ +# Check handling of R_MICROMIPS_PC7_S1, R_MICROMIPS_PC10_S1, +# R_MICROMIPS_PC16_S1, and R_MICROMIPS_PC23_S2 relocations. + +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: lld -flavor gnu -target mipsel -o %t-exe %t-obj +# RUN: llvm-objdump -s -t %t-exe | FileCheck %s + +# CHECK: Contents of section .text: +# CHECK-NEXT: 400110 00000000 80780500 a240fcff 000c03cc .....x...@...... +# ^^ addiu s1,$pc,20 +# ^^ bnezc v0,400114 <__start+0x4> +# ^^ b 400126 <L1> +# CHECK-NEXT: 400120 000c03ad 00000000 00000000 00000000 ................ +# ^^ bnez v0,40012a <L2> +# CHECK: SYMBOL TABLE: +# CHECK: 00400124 l F .text 00000002 L0 +# CHECK: 00400126 l F .text 00000004 L1 +# CHECK: 0040012a l F .text 00000004 L2 +# CHECK: 0040012e l F .text 00000002 L3 +# CHECK: 00400110 g F .text 00000014 __start + +!ELF +FileHeader: !FileHeader + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_CPIC, EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, + EF_MIPS_MICROMIPS ] + +Sections: +- Name: .text + Type: SHT_PROGBITS +# v nop v nop v L0 + Content: "0000000080780100a240f5ff000cfdcf000c7dad000000000000000000000000" +# ^ PC23 ^ PC16 ^ PC10 ^ PC7 ^ L1 ^ L2 ^ L3 +# 7d << 1 = -6 => L3 + 2 - 6 = L2 +# 3fd << 1 = -6 => L2 + 2 - 6 = L1 +# fff5 << 1 = -22 => L1 + 2 - 22 = __start +# 1 << 2 = 4 => L0 + 4 - 4 = L0 + AddressAlign: 16 + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + +- Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 4 + Symbol: L0 + Type: R_MICROMIPS_PC23_S2 + - Offset: 8 + Symbol: L1 + Type: R_MICROMIPS_PC16_S1 + - Offset: 14 + Symbol: L2 + Type: R_MICROMIPS_PC10_S1 + - Offset: 18 + Symbol: L3 + Type: R_MICROMIPS_PC7_S1 + +Symbols: + Local: + - Name: L0 + Section: .text + Value: 20 + Other: [ STO_MIPS_MICROMIPS ] + - Name: L1 + Section: .text + Value: 22 + Other: [ STO_MIPS_MICROMIPS ] + - Name: L2 + Section: .text + Value: 26 + Other: [ STO_MIPS_MICROMIPS ] + - Name: L3 + Section: .text + Value: 30 + Other: [ STO_MIPS_MICROMIPS ] + Global: + - Name: __start + Section: .text + Type: STT_FUNC + Value: 0x0 + Size: 32 + Other: [ STO_MIPS_MICROMIPS ] diff --git a/test/elf/Mips/rel-sub.test b/test/elf/Mips/rel-sub.test new file mode 100644 index 0000000000000..93e569a22035f --- /dev/null +++ b/test/elf/Mips/rel-sub.test @@ -0,0 +1,61 @@ +# Check handling of R_MIPS_SUB relocation. + +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target mips64el -o %t.exe %t.o +# RUN: llvm-objdump -s -t %t.exe | FileCheck %s + +# CHECK: Contents of section .data: +# CHECK-NEXT: 120002000 cf010020 01000000 d0010020 0100ffff ... ....... .... +# ^^ __start - 1 = 0x1200001cf +# ^^ __start - 0x1000000000000 +# = 0Xffff0001200001d0 +# CHECK: SYMBOL TABLE: +# CHECK: 00000001200001d0 g .rodata 00000008 __start + +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ARCH_64] + +Sections: +- Name: .text + Type: SHT_PROGBITS + Size: 0x08 + AddressAlign: 16 + Flags: [SHF_ALLOC] +- Name: .data + Type: SHT_PROGBITS + Size: 0x10 + AddressAlign: 16 + Flags: [SHF_ALLOC, SHF_WRITE] + +- Name: .rela.data + Type: SHT_RELA + Info: .data + AddressAlign: 4 + Relocations: + - Offset: 0x0 + Symbol: __start + Type: R_MIPS_SUB + Addend: 1 + - Offset: 0x8 + Symbol: __start + Type: R_MIPS_SUB + Addend: 0x1000000000000 + +Symbols: + Global: + - Name: __start + Section: .text + Value: 0x0 + Size: 8 + - Name: data1 + Section: .data + Value: 0x0 + Size: 8 + - Name: data2 + Section: .data + Value: 0x8 + Size: 8 diff --git a/test/elf/Mips/st-other.test b/test/elf/Mips/st-other.test new file mode 100644 index 0000000000000..8d15e75676b0b --- /dev/null +++ b/test/elf/Mips/st-other.test @@ -0,0 +1,90 @@ +# Check STO_MICROMIPS flag handling. microMIPS symbol records in a dynamic +# symbol table should not have STO_MICROMIPS flag but their value field +# must be odd. microMIPS symbol records in a regular symbol table should +# have the STO_MICROMIPS flag. + +# RUN: yaml2obj -format=elf %s > %t-micro.o + +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-micro.o +# RUN: llvm-readobj -dyn-symbols %t.so | FileCheck -check-prefix=SO %s + +# RUN: lld -flavor gnu -target mipsel -e S0 -o %t.exe %t-micro.o +# RUN: llvm-readobj -symbols %t.exe | FileCheck -check-prefix=EXE-SYM %s +# RUN: llvm-readobj -dyn-symbols %t.exe | FileCheck -check-prefix=EXE-DSYM %s + +# SO: Symbol { +# SO: Name: S0@ (1) +# SO-NEXT: Value: 0xEC +# SO-NEXT: Size: 4 +# SO-NEXT: Binding: Global (0x1) +# SO-NEXT: Type: Function (0x2) +# SO-NEXT: Other: 0 +# SO-NEXT: Section: .text (0x4) +# SO-NEXT: } + +# SO: Symbol { +# SO: Name: S1@ (4) +# SO-NEXT: Value: 0xF1 +# SO-NEXT: Size: 4 +# SO-NEXT: Binding: Global (0x1) +# SO-NEXT: Type: Function (0x2) +# SO-NEXT: Other: 0 +# SO-NEXT: Section: .text (0x4) +# SO-NEXT: } + +# EXE-SYM: Symbol { +# EXE-SYM: Name: S0 (1) +# EXE-SYM-NEXT: Value: 0x400108 +# EXE-SYM-NEXT: Size: 4 +# EXE-SYM-NEXT: Binding: Global (0x1) +# EXE-SYM-NEXT: Type: Function (0x2) +# EXE-SYM-NEXT: Other: 0 +# EXE-SYM-NEXT: Section: .text (0x5) +# EXE-SYM-NEXT: } + +# EXE-SYM: Symbol { +# EXE-SYM: Name: S1 (4) +# EXE-SYM-NEXT: Value: 0x40010D +# EXE-SYM-NEXT: Size: 4 +# EXE-SYM-NEXT: Binding: Global (0x1) +# EXE-SYM-NEXT: Type: Function (0x2) +# EXE-SYM-NEXT: Other: 128 +# EXE-SYM-NEXT: Section: .text (0x5) +# EXE-SYM-NEXT: } + +# EXE-DSYM-NOT: Name: S1 (4) + +# micro.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x08 + +Symbols: + Global: + - Name: S0 + Type: STT_FUNC + Section: .text + Size: 0x04 + Value: 0x00 + Visibility: STV_DEFAULT + Other: [ ] + + - Name: S1 + Type: STT_FUNC + Section: .text + Size: 0x04 + Value: 0x04 + Visibility: STV_DEFAULT + Other: [ STO_MIPS_MICROMIPS ] +... diff --git a/test/elf/Mips/tls-1-micro.test b/test/elf/Mips/tls-1-micro.test new file mode 100644 index 0000000000000..bd962b4e9e804 --- /dev/null +++ b/test/elf/Mips/tls-1-micro.test @@ -0,0 +1,65 @@ +# Check handling of R_MICROMIPS_TLS_TPREL_HI16 / R_MICROMIPS_TLS_TPREL_LO16 +# relocations. + +# RUN: yaml2obj -format=elf -o %t.o %s +# RUN: lld -flavor gnu -target mipsel -e L0 -o %t.exe %t.o +# RUN: llvm-objdump -s %t.exe | FileCheck %s + +# CHECK: Contents of section .text: +# CHECK: 400150 00000000 00000100 00000380 00000480 ................ + +!ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Content: '00000100000002000000030000000400' + - Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 0x04 + Relocations: + - Offset: 0x00 + Symbol: L1 + Type: R_MICROMIPS_TLS_TPREL_HI16 + - Offset: 0x04 + Symbol: L2 + Type: R_MICROMIPS_TLS_TPREL_HI16 + - Offset: 0x08 + Symbol: L2 + Type: R_MICROMIPS_TLS_TPREL_LO16 + - Offset: 0x0C + Symbol: L1 + Type: R_MICROMIPS_TLS_TPREL_LO16 + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 0x04 + Address: 0x1000 + Size: 0x20000 + +Symbols: + Global: + - Name: L0 + Type: STT_FUNC + Section: .text + Size: 0x58 + Other: [ STO_MIPS_MICROMIPS ] + - Name: L1 + Type: STT_TLS + Section: .tdata + Value: 0x00 + Size: 0x04 + - Name: L2 + Type: STT_TLS + Section: .tdata + Value: 0x10000 + Size: 0x04 diff --git a/test/elf/Mips/tls-1.test b/test/elf/Mips/tls-1.test new file mode 100644 index 0000000000000..99176e69171d5 --- /dev/null +++ b/test/elf/Mips/tls-1.test @@ -0,0 +1,63 @@ +# Check handling of R_MIPS_TLS_TPREL_HI16 / R_MIPS_TLS_TPREL_LO16 relocations. + +# RUN: yaml2obj -format=elf -o %t.o %s +# RUN: lld -flavor gnu -target mipsel -e L0 -o %t.exe %t.o +# RUN: llvm-objdump -s %t.exe | FileCheck %s + +# CHECK: Contents of section .text: +# CHECK: 400150 00000000 01000000 03800000 04800000 ................ + +!ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Content: '01000000020000000300000004000000' + - Name: .rel.text + Type: SHT_REL + Info: .text + AddressAlign: 0x04 + Relocations: + - Offset: 0x00 + Symbol: L1 + Type: R_MIPS_TLS_TPREL_HI16 + - Offset: 0x04 + Symbol: L2 + Type: R_MIPS_TLS_TPREL_HI16 + - Offset: 0x08 + Symbol: L2 + Type: R_MIPS_TLS_TPREL_LO16 + - Offset: 0x0C + Symbol: L1 + Type: R_MIPS_TLS_TPREL_LO16 + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 0x04 + Address: 0x1000 + Size: 0x20000 + +Symbols: + Global: + - Name: L0 + Type: STT_FUNC + Section: .text + Size: 0x58 + - Name: L1 + Type: STT_TLS + Section: .tdata + Value: 0x00 + Size: 0x04 + - Name: L2 + Type: STT_TLS + Section: .tdata + Value: 0x10000 + Size: 0x04 diff --git a/test/elf/Mips/tls-2-64.test b/test/elf/Mips/tls-2-64.test new file mode 100644 index 0000000000000..a068934aeafa5 --- /dev/null +++ b/test/elf/Mips/tls-2-64.test @@ -0,0 +1,69 @@ +# Check handling of R_MIPS_TLS_GOTTPREL and R_MIPS_TLS_GD relocations +# and generation of corresponding dynamic relocations R_MIPS_TLS_TPREL64, +# R_MIPS_TLS_DTPMOD64 and R_MIPS_TLS_DTPREL64 in case of shared library. + +# Create a shared library with thread symbol D1. +# RUN: yaml2obj -format=elf -o %t-so.o %s +# RUN: lld -flavor gnu -target mips64el -shared -o %t.so %t-so.o + +# Check dynamic relocations and GOT in the shared library. +# RUN: llvm-readobj -r %t.so | FileCheck -check-prefix=REL %s +# RUN: llvm-readobj -dynamic-table %t.so | FileCheck -check-prefix=DYN %s +# RUN: llvm-readobj -dt %t.so | FileCheck -check-prefix=SYM %s +# RUN: llvm-objdump -s %t.so | FileCheck -check-prefix=GOT %s + +# REL: Section (4) .rel.dyn { +# REL-NEXT: 0x2010 R_MIPS_TLS_DTPMOD64/R_MIPS_NONE/R_MIPS_NONE D1 0x0 +# REL-NEXT: 0x2018 R_MIPS_TLS_DTPREL64/R_MIPS_NONE/R_MIPS_NONE D1 0x0 +# REL-NEXT: } + +# DYN: 0x000000007000000A MIPS_LOCAL_GOTNO 2 +# DYN: 0x0000000070000013 MIPS_GOTSYM 0x3 + +# SYM: Name: T1@ (1) +# SYM: Name: D1@ (4) + +# GOT: Contents of section .got: +# GOT-NEXT: 2000 00000000 00000000 00000000 00000080 ................ +# GOT-NEXT: 2010 00000000 00000000 00000000 00000000 ................ + +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ARCH_64] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 4 + Size: 4 + - Name: .rel.text + Type: SHT_RELA + Link: .symtab + Info: .text + AddressAlign: 4 + Relocations: + - Offset: 0 + Symbol: D1 + Type: R_MIPS_TLS_GD + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 4 + Size: 8 + +Symbols: + Global: + - Name: T1 + Type: STT_FUNC + Section: .text + Size: 4 + - Name: D1 + Type: STT_TLS + Section: .tdata + Size: 8 +... diff --git a/test/elf/Mips/tls-2-micro.test b/test/elf/Mips/tls-2-micro.test new file mode 100644 index 0000000000000..5a1fe2904ac96 --- /dev/null +++ b/test/elf/Mips/tls-2-micro.test @@ -0,0 +1,70 @@ +# Check handling of R_MICROMIPS_TLS_GOTTPREL and R_MICROMIPS_TLS_GD relocations +# and generation of corresponding dynamic relocations R_MIPS_TLS_TPREL32, +# R_MIPS_TLS_DTPMOD32 and R_MIPS_TLS_DTPREL32 in case of shared library. + +# Create a shared library with thread symbol D1. +# RUN: yaml2obj -format=elf -o %t-so.o %s +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o + +# Check dynamic relocations and GOT in the shared library. +# RUN: llvm-readobj -r %t.so | FileCheck -check-prefix=REL %s +# RUN: llvm-readobj -dynamic-table %t.so | FileCheck -check-prefix=DYN %s +# RUN: llvm-readobj -dt %t.so | FileCheck -check-prefix=SYM %s +# RUN: llvm-objdump -s %t.so | FileCheck -check-prefix=GOT %s + +# REL: Section (4) .rel.dyn { +# REL-NEXT: 0x2008 R_MIPS_TLS_DTPMOD32 D1 0x0 +# REL-NEXT: 0x200C R_MIPS_TLS_DTPREL32 D1 0x0 +# REL-NEXT: } + +# DYN: 0x7000000A MIPS_LOCAL_GOTNO 2 +# DYN: 0x70000013 MIPS_GOTSYM 0x3 + +# SYM: Name: T1@ (1) +# SYM: Name: D1@ (4) + +# GOT: Contents of section .got: +# GOT-NEXT: 2000 00000000 00000080 00000000 00000000 ................ + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + Info: .text + AddressAlign: 0x04 + Relocations: + - Offset: 0x00 + Symbol: D1 + Type: R_MICROMIPS_TLS_GD + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Global: + - Name: T1 + Type: STT_FUNC + Section: .text + Size: 0x04 + Other: [ STO_MIPS_MICROMIPS ] + - Name: D1 + Type: STT_TLS + Section: .tdata + Size: 0x04 +... diff --git a/test/elf/Mips/tls-2.test b/test/elf/Mips/tls-2.test new file mode 100644 index 0000000000000..32b2bc1051122 --- /dev/null +++ b/test/elf/Mips/tls-2.test @@ -0,0 +1,69 @@ +# Check handling of R_MIPS_TLS_GOTTPREL and R_MIPS_TLS_GD relocations +# and generation of corresponding dynamic relocations R_MIPS_TLS_TPREL32, +# R_MIPS_TLS_DTPMOD32 and R_MIPS_TLS_DTPREL32 in case of shared library. + +# Create a shared library with thread symbol D1. +# RUN: yaml2obj -format=elf -o %t-so.o %s +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o + +# Check dynamic relocations and GOT in the shared library. +# RUN: llvm-readobj -r %t.so | FileCheck -check-prefix=REL %s +# RUN: llvm-readobj -dynamic-table %t.so | FileCheck -check-prefix=DYN %s +# RUN: llvm-readobj -dt %t.so | FileCheck -check-prefix=SYM %s +# RUN: llvm-objdump -s %t.so | FileCheck -check-prefix=GOT %s + +# REL: Section (4) .rel.dyn { +# REL-NEXT: 0x2008 R_MIPS_TLS_DTPMOD32 D1 0x0 +# REL-NEXT: 0x200C R_MIPS_TLS_DTPREL32 D1 0x0 +# REL-NEXT: } + +# DYN: 0x7000000A MIPS_LOCAL_GOTNO 2 +# DYN: 0x70000013 MIPS_GOTSYM 0x3 + +# SYM: Name: T1@ (1) +# SYM: Name: D1@ (4) + +# GOT: Contents of section .got: +# GOT-NEXT: 2000 00000000 00000080 00000000 00000000 ................ + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + Info: .text + AddressAlign: 0x04 + Relocations: + - Offset: 0x00 + Symbol: D1 + Type: R_MIPS_TLS_GD + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Global: + - Name: T1 + Type: STT_FUNC + Section: .text + Size: 0x04 + - Name: D1 + Type: STT_TLS + Section: .tdata + Size: 0x04 +... diff --git a/test/elf/Mips/tls-3-micro.test b/test/elf/Mips/tls-3-micro.test new file mode 100644 index 0000000000000..0e0f3d556f8f3 --- /dev/null +++ b/test/elf/Mips/tls-3-micro.test @@ -0,0 +1,183 @@ +# Check handling of R_MICROMIPS_TLS_GOTTPREL and R_MICROMIPS_TLS_GD relocations +# and generation of corresponding dynamic relocations R_MIPS_TLS_TPREL32, +# R_MIPS_TLS_DTPMOD32 and R_MIPS_TLS_DTPREL32 in case of executable linking. + +# Create a shared library with thread symbol D1. +# RUN: yaml2obj -format=elf -docnum 1 -o %t-so.o %s +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o + +# Create executable file linked using two object files and the shared library. +# The object files defines thread symbols D0 and D2. +# RUN: yaml2obj -format=elf -docnum 2 -o %t-o1.o %s +# RUN: yaml2obj -format=elf -docnum 3 -o %t-o2.o %s +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o1.o %t-o2.o %t.so + +# Check dynamic relocations and GOT in the executable file. +# RUN: llvm-readobj -r %t.exe | FileCheck -check-prefix=REL %s +# RUN: llvm-readobj -dynamic-table %t.exe | FileCheck -check-prefix=DYN %s +# RUN: llvm-readobj -dt %t.exe | FileCheck -check-prefix=SYM %s +# RUN: llvm-objdump -s %t.exe | FileCheck -check-prefix=GOT %s + +# REL: Section (5) .rel.dyn { +# REL-NEXT: 0x402008 R_MIPS_TLS_TPREL32 D1 0x0 +# REL-NEXT: 0x40200C R_MIPS_TLS_TPREL32 D2 0x0 +# REL-NEXT: } + +# DYN: 0x7000000A MIPS_LOCAL_GOTNO 2 +# DYN: 0x70000013 MIPS_GOTSYM 0x3 + +# SYM: Name: D2@ (1) +# SYM: Name: D1@ (4) + +# GOT: Contents of section .got: +# GOT-NEXT: 402000 00000000 00000080 00000000 00000000 ................ + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + Info: .text + AddressAlign: 0x04 + Relocations: + - Offset: 0x00 + Symbol: D1 + Type: R_MICROMIPS_TLS_GD + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Global: + - Name: T1 + Type: STT_FUNC + Section: .text + Size: 0x04 + Other: [ STO_MIPS_MICROMIPS ] + - Name: D1 + Type: STT_TLS + Section: .tdata + Size: 0x04 + +# o1.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x08 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + Info: .text + AddressAlign: 0x04 + Relocations: + - Offset: 0x00 + Symbol: D2 + Type: R_MICROMIPS_TLS_TPREL_HI16 + - Offset: 0x04 + Symbol: D2 + Type: R_MICROMIPS_TLS_TPREL_LO16 + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Global: + - Name: T2 + Type: STT_FUNC + Section: .text + Size: 0x08 + Other: [ STO_MIPS_MICROMIPS ] + - Name: D2 + Type: STT_TLS + Section: .tdata + Size: 0x04 + +# o2.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x10 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + Info: .text + AddressAlign: 0x04 + Relocations: + - Offset: 0x00 + Symbol: D1 + Type: R_MICROMIPS_TLS_GOTTPREL + Addend: 0 + - Offset: 0x04 + Symbol: D0 + Type: R_MICROMIPS_TLS_TPREL_HI16 + Addend: 0 + - Offset: 0x08 + Symbol: D0 + Type: R_MICROMIPS_TLS_TPREL_LO16 + Addend: 0 + - Offset: 0x0C + Symbol: D2 + Type: R_MICROMIPS_TLS_GOTTPREL + Addend: 0 + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Global: + - Name: D0 + Type: STT_TLS + Section: .tdata + Size: 0x04 + - Name: T0 + Type: STT_FUNC + Section: .text + Size: 0x10 + Other: [ STO_MIPS_MICROMIPS ] + - Name: D1 + Type: STT_TLS + - Name: D2 + Type: STT_TLS +... diff --git a/test/elf/Mips/tls-3.test b/test/elf/Mips/tls-3.test new file mode 100644 index 0000000000000..7e54724fade5a --- /dev/null +++ b/test/elf/Mips/tls-3.test @@ -0,0 +1,180 @@ +# Check handling of R_MIPS_TLS_GOTTPREL and R_MIPS_TLS_GD relocations +# and generation of corresponding dynamic relocations R_MIPS_TLS_TPREL32, +# R_MIPS_TLS_DTPMOD32 and R_MIPS_TLS_DTPREL32 in case of executable linking. + +# Create a shared library with thread symbol D1. +# RUN: yaml2obj -format=elf -docnum 1 -o %t-so.o %s +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so.o + +# Create executable file linked using two object files and the shared library. +# The object files defines thread symbols D0 and D2. +# RUN: yaml2obj -format=elf -docnum 2 -o %t-o1.o %s +# RUN: yaml2obj -format=elf -docnum 3 -o %t-o2.o %s +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o1.o %t-o2.o %t.so + +# Check dynamic relocations and GOT in the executable file. +# RUN: llvm-readobj -r %t.exe | FileCheck -check-prefix=REL %s +# RUN: llvm-readobj -dynamic-table %t.exe | FileCheck -check-prefix=DYN %s +# RUN: llvm-readobj -dt %t.exe | FileCheck -check-prefix=SYM %s +# RUN: llvm-objdump -s %t.exe | FileCheck -check-prefix=GOT %s + +# REL: Section (5) .rel.dyn { +# REL-NEXT: 0x402008 R_MIPS_TLS_TPREL32 D1 0x0 +# REL-NEXT: 0x40200C R_MIPS_TLS_TPREL32 D2 0x0 +# REL-NEXT: } + +# DYN: 0x7000000A MIPS_LOCAL_GOTNO 2 +# DYN: 0x70000013 MIPS_GOTSYM 0x3 + +# SYM: Name: D2@ (1) +# SYM: Name: D1@ (4) + +# GOT: Contents of section .got: +# GOT-NEXT: 402000 00000000 00000080 00000000 00000000 ................ + +# so.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + Info: .text + AddressAlign: 0x04 + Relocations: + - Offset: 0x00 + Symbol: D1 + Type: R_MIPS_TLS_GD + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Global: + - Name: T1 + Type: STT_FUNC + Section: .text + Size: 0x04 + - Name: D1 + Type: STT_TLS + Section: .tdata + Size: 0x04 + +# o1.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x08 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + Info: .text + AddressAlign: 0x04 + Relocations: + - Offset: 0x00 + Symbol: D2 + Type: R_MIPS_TLS_TPREL_HI16 + - Offset: 0x04 + Symbol: D2 + Type: R_MIPS_TLS_TPREL_LO16 + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Global: + - Name: T2 + Type: STT_FUNC + Section: .text + Size: 0x08 + - Name: D2 + Type: STT_TLS + Section: .tdata + Size: 0x04 + +# o2.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x10 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + Info: .text + AddressAlign: 0x04 + Relocations: + - Offset: 0x00 + Symbol: D1 + Type: R_MIPS_TLS_GOTTPREL + Addend: 0 + - Offset: 0x04 + Symbol: D0 + Type: R_MIPS_TLS_TPREL_HI16 + Addend: 0 + - Offset: 0x08 + Symbol: D0 + Type: R_MIPS_TLS_TPREL_LO16 + Addend: 0 + - Offset: 0x0C + Symbol: D2 + Type: R_MIPS_TLS_GOTTPREL + Addend: 0 + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Global: + - Name: D0 + Type: STT_TLS + Section: .tdata + Size: 0x04 + - Name: T0 + Type: STT_FUNC + Section: .text + Size: 0x10 + - Name: D1 + Type: STT_TLS + - Name: D2 + Type: STT_TLS +... diff --git a/test/elf/Mips/tls-4-micro.test b/test/elf/Mips/tls-4-micro.test new file mode 100644 index 0000000000000..1b0d03fa88753 --- /dev/null +++ b/test/elf/Mips/tls-4-micro.test @@ -0,0 +1,126 @@ +# Check handling of R_MICROMIPS_TLS_LDM relocation and generation +# of corresponding dynamic relocation R_MICROMIPS_TLS_DTPMOD32. + +# RUN: yaml2obj -format=elf -docnum 1 -o %t-so1.o %s +# RUN: yaml2obj -format=elf -docnum 2 -o %t-so2.o %s +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so1.o %t-so2.o + +# RUN: llvm-readobj -r %t.so | FileCheck -check-prefix=REL %s +# RUN: llvm-readobj -dynamic-table %t.so | FileCheck -check-prefix=DYN %s +# RUN: llvm-readobj -dt %t.so | FileCheck -check-prefix=SYM %s +# RUN: llvm-objdump -s %t.so | FileCheck -check-prefix=GOT %s + +# REL: Section (4) .rel.dyn { +# REL-NEXT: 0x2008 R_MIPS_TLS_DTPMOD32 - 0x0 +# REL-NEXT: } + +# DYN: 0x7000000A MIPS_LOCAL_GOTNO 2 +# DYN: 0x70000013 MIPS_GOTSYM 0x4 + +# SYM: Name: @ (0) +# SYM: Name: T1@ (1) +# SYM: Name: T2@ (4) +# SYM: Name: T3@ (7) + +# GOT: Contents of section .got: +# GOT-NEXT: 2000 00000000 00000080 00000000 00000000 ................ +# Two LDM entries --^--------^ + +# so1.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x08 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: L01 + Type: R_MICROMIPS_TLS_LDM + - Offset: 0x04 + Symbol: L01 + Type: R_MICROMIPS_TLS_LDM + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Local: + - Name: L01 + Type: STT_TLS + Section: .tdata + Size: 0x04 + Global: + - Name: T1 + Type: STT_FUNC + Section: .text + Value: 0x00 + Size: 0x04 + Other: [ STO_MIPS_MICROMIPS ] + - Name: T2 + Type: STT_FUNC + Section: .text + Value: 0x04 + Size: 0x04 + Other: [ STO_MIPS_MICROMIPS ] + +# so2.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: L02 + Type: R_MICROMIPS_TLS_LDM + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Local: + - Name: L02 + Type: STT_TLS + Section: .tdata + Size: 0x04 + Global: + - Name: T3 + Type: STT_FUNC + Section: .text + Size: 0x04 + Other: [ STO_MIPS_MICROMIPS ] +... diff --git a/test/elf/Mips/tls-4.test b/test/elf/Mips/tls-4.test new file mode 100644 index 0000000000000..fb42f0d93378c --- /dev/null +++ b/test/elf/Mips/tls-4.test @@ -0,0 +1,123 @@ +# Check handling of R_MIPS_TLS_LDM relocation and generation of corresponding +# dynamic relocation R_MIPS_TLS_DTPMOD32. + +# RUN: yaml2obj -format=elf -docnum 1 -o %t-so1.o %s +# RUN: yaml2obj -format=elf -docnum 2 -o %t-so2.o %s +# RUN: lld -flavor gnu -target mipsel -shared -o %t.so %t-so1.o %t-so2.o + +# RUN: llvm-readobj -r %t.so | FileCheck -check-prefix=REL %s +# RUN: llvm-readobj -dynamic-table %t.so | FileCheck -check-prefix=DYN %s +# RUN: llvm-readobj -dt %t.so | FileCheck -check-prefix=SYM %s +# RUN: llvm-objdump -s %t.so | FileCheck -check-prefix=GOT %s + +# REL: Section (4) .rel.dyn { +# REL-NEXT: 0x2008 R_MIPS_TLS_DTPMOD32 - 0x0 +# REL-NEXT: } + +# DYN: 0x7000000A MIPS_LOCAL_GOTNO 2 +# DYN: 0x70000013 MIPS_GOTSYM 0x4 + +# SYM: Name: @ (0) +# SYM: Name: T1@ (1) +# SYM: Name: T2@ (4) +# SYM: Name: T3@ (7) + +# GOT: Contents of section .got: +# GOT-NEXT: 2000 00000000 00000080 00000000 00000000 ................ +# Two LDM entries --^--------^ + +# so1.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x08 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: L01 + Type: R_MIPS_TLS_LDM + - Offset: 0x04 + Symbol: L01 + Type: R_MIPS_TLS_LDM + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Local: + - Name: L01 + Type: STT_TLS + Section: .tdata + Size: 0x04 + Global: + - Name: T1 + Type: STT_FUNC + Section: .text + Value: 0x00 + Size: 0x04 + - Name: T2 + Type: STT_FUNC + Section: .text + Value: 0x04 + Size: 0x04 + +# so2.o +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x04 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: L02 + Type: R_MIPS_TLS_LDM + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Local: + - Name: L02 + Type: STT_TLS + Section: .tdata + Size: 0x04 + Global: + - Name: T3 + Type: STT_FUNC + Section: .text + Size: 0x04 +... diff --git a/test/elf/Mips/tls-5-64.test b/test/elf/Mips/tls-5-64.test new file mode 100644 index 0000000000000..784d71efc48f4 --- /dev/null +++ b/test/elf/Mips/tls-5-64.test @@ -0,0 +1,71 @@ +# Check that in case of an executable file linking symbol referred +# by the R_MIPS_TLS_GD relocation gets an entry in the dynamic symbol table. + +# RUN: yaml2obj -format=elf -o %t-o.o %s +# RUN: lld -flavor gnu -target mips64el -e T0 -o %t.exe %t-o.o + +# Check dynamic relocations: +# RUN: llvm-readobj -r %t.exe | FileCheck -check-prefix=REL %s +# Check dynamic symbol table: +# RUN: llvm-readobj -dt %t.exe | FileCheck -check-prefix=SYM %s + +# REL: Relocations [ +# REL-NEXT: Section (5) .rel.dyn { +# REL-NEXT: 0x120002010 R_MIPS_TLS_DTPMOD64/R_MIPS_NONE/R_MIPS_NONE T1 0x0 +# REL-NEXT: 0x120002018 R_MIPS_TLS_DTPREL64/R_MIPS_NONE/R_MIPS_NONE T1 0x0 +# REL-NEXT: } +# REL-NEXT: ] + +# SYM: Symbol { +# SYM: Name: T1@ (1) +# SYM-NEXT: Value: 0x0 +# SYM-NEXT: Size: 8 +# SYM-NEXT: Binding: Global (0x1) +# SYM-NEXT: Type: TLS (0x6) +# SYM-NEXT: Other: 0 +# SYM-NEXT: Section: .tdata (0x7) +# SYM-NEXT: } + +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, EF_MIPS_ARCH_64] + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 8 + + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 4 + Info: .text + Relocations: + - Offset: 0 + Symbol: T1 + Type: R_MIPS_TLS_GD + + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 4 + Size: 8 + +Symbols: + Global: + - Name: T0 + Type: STT_FUNC + Section: .text + Size: 8 + - Name: T1 + Type: STT_TLS + Section: .tdata + Value: 0 + Size: 8 +... diff --git a/test/elf/Mips/tls-5-micro.test b/test/elf/Mips/tls-5-micro.test new file mode 100644 index 0000000000000..89d1d98a6877f --- /dev/null +++ b/test/elf/Mips/tls-5-micro.test @@ -0,0 +1,70 @@ +# Check that in case of an executable file linking symbol referred by +# the R_MICROMIPS_TLS_GD relocation gets an entry in the dynamic symbol table. + +# RUN: yaml2obj -format=elf -o %t-o.o %s +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o.o + +# Check dynamic relocations: +# RUN: llvm-readobj -r %t.exe | FileCheck -check-prefix=REL %s +# Check dynamic symbol table: +# RUN: llvm-readobj -dt %t.exe | FileCheck -check-prefix=SYM %s + +# REL: Relocations [ +# REL-NEXT: Section (5) .rel.dyn { +# REL-NEXT: 0x402008 R_MIPS_TLS_DTPMOD32 T1 0x0 +# REL-NEXT: 0x40200C R_MIPS_TLS_DTPREL32 T1 0x0 +# REL-NEXT: } +# REL-NEXT: ] + +# SYM: Symbol { +# SYM: Name: T1@ (1) +# SYM-NEXT: Value: 0x0 +# SYM-NEXT: Size: 4 +# SYM-NEXT: Binding: Global (0x1) +# SYM-NEXT: Type: TLS (0x6) +# SYM-NEXT: Other: 0 +# SYM-NEXT: Section: .tdata (0x7) +# SYM-NEXT: } + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2, EF_MIPS_MICROMIPS ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x04 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: T1 + Type: R_MICROMIPS_TLS_GD + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Global: + - Name: T0 + Type: STT_FUNC + Section: .text + Size: 0x04 + Other: [ STO_MIPS_MICROMIPS ] + - Name: T1 + Type: STT_TLS + Section: .tdata + Value: 0x00 + Size: 0x04 +... diff --git a/test/elf/Mips/tls-5.test b/test/elf/Mips/tls-5.test new file mode 100644 index 0000000000000..378ce321b8c92 --- /dev/null +++ b/test/elf/Mips/tls-5.test @@ -0,0 +1,69 @@ +# Check that in case of an executable file linking symbol referred +# by the R_MIPS_TLS_GD relocation gets an entry in the dynamic symbol table. + +# RUN: yaml2obj -format=elf -o %t-o.o %s +# RUN: lld -flavor gnu -target mipsel -e T0 -o %t.exe %t-o.o + +# Check dynamic relocations: +# RUN: llvm-readobj -r %t.exe | FileCheck -check-prefix=REL %s +# Check dynamic symbol table: +# RUN: llvm-readobj -dt %t.exe | FileCheck -check-prefix=SYM %s + +# REL: Relocations [ +# REL-NEXT: Section (5) .rel.dyn { +# REL-NEXT: 0x402008 R_MIPS_TLS_DTPMOD32 T1 0x0 +# REL-NEXT: 0x40200C R_MIPS_TLS_DTPREL32 T1 0x0 +# REL-NEXT: } +# REL-NEXT: ] + +# SYM: Symbol { +# SYM: Name: T1@ (1) +# SYM-NEXT: Value: 0x0 +# SYM-NEXT: Size: 4 +# SYM-NEXT: Binding: Global (0x1) +# SYM-NEXT: Type: TLS (0x6) +# SYM-NEXT: Other: 0 +# SYM-NEXT: Section: .tdata (0x7) +# SYM-NEXT: } + +--- +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_MIPS + Flags: [ EF_MIPS_NOREORDER, EF_MIPS_PIC, EF_MIPS_CPIC, + EF_MIPS_ABI_O32, EF_MIPS_ARCH_32R2 ] +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x10 + Size: 0x04 + - Name: .rel.text + Type: SHT_REL + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: T1 + Type: R_MIPS_TLS_GD + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Global: + - Name: T0 + Type: STT_FUNC + Section: .text + Size: 0x04 + - Name: T1 + Type: STT_TLS + Section: .tdata + Value: 0x00 + Size: 0x04 +... diff --git a/test/elf/X86_64/ExampleTarget/triple.test b/test/elf/X86_64/ExampleTarget/triple.test new file mode 100644 index 0000000000000..3aecceb4305e3 --- /dev/null +++ b/test/elf/X86_64/ExampleTarget/triple.test @@ -0,0 +1,32 @@ +# Check that the Example Target is actually used. + +# RUN: yaml2obj -format=elf %s -o %t.o +# RUN: lld -flavor gnu -target x86_64-example-freebsd9 %t.o -o %t.exe +# RUN: llvm-readobj -file-headers %t.exe | FileCheck %s +# +# CHECK: Type: 0xFF00 + +# object + +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E5B864000000C745FC000000005DC366666666662E0F1F840000000000554889E531C05DC3 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + Global: + - Name: _start + Type: STT_FUNC + Section: .text + Size: 0x0000000000000000 diff --git a/test/elf/X86_64/Inputs/constint.c b/test/elf/X86_64/Inputs/constint.c new file mode 100644 index 0000000000000..9fc0ebcf6591b --- /dev/null +++ b/test/elf/X86_64/Inputs/constint.c @@ -0,0 +1 @@ +const int b = 20; diff --git a/test/elf/X86_64/Inputs/constint.o b/test/elf/X86_64/Inputs/constint.o Binary files differnew file mode 100644 index 0000000000000..8324b73014d08 --- /dev/null +++ b/test/elf/X86_64/Inputs/constint.o diff --git a/test/elf/X86_64/Inputs/debug0.c b/test/elf/X86_64/Inputs/debug0.c new file mode 100644 index 0000000000000..1ebe500873837 --- /dev/null +++ b/test/elf/X86_64/Inputs/debug0.c @@ -0,0 +1,5 @@ +int adena(); + +int main() { +return adena(); +} diff --git a/test/elf/X86_64/Inputs/debug0.x86-64 b/test/elf/X86_64/Inputs/debug0.x86-64 Binary files differnew file mode 100644 index 0000000000000..914f5224b9a2f --- /dev/null +++ b/test/elf/X86_64/Inputs/debug0.x86-64 diff --git a/test/elf/X86_64/Inputs/debug1.c b/test/elf/X86_64/Inputs/debug1.c new file mode 100644 index 0000000000000..281b8a361dbb3 --- /dev/null +++ b/test/elf/X86_64/Inputs/debug1.c @@ -0,0 +1,3 @@ +int adena() { +return 0; +} diff --git a/test/elf/X86_64/Inputs/debug1.x86-64 b/test/elf/X86_64/Inputs/debug1.x86-64 Binary files differnew file mode 100644 index 0000000000000..bfc81458a0348 --- /dev/null +++ b/test/elf/X86_64/Inputs/debug1.x86-64 diff --git a/test/elf/X86_64/Inputs/externtls.c b/test/elf/X86_64/Inputs/externtls.c new file mode 100644 index 0000000000000..499a645a92175 --- /dev/null +++ b/test/elf/X86_64/Inputs/externtls.c @@ -0,0 +1,6 @@ +extern __thread int extern_tls; + +int main() { + extern_tls = 1; + return 0; +} diff --git a/test/elf/X86_64/Inputs/externtls.x86-64 b/test/elf/X86_64/Inputs/externtls.x86-64 Binary files differnew file mode 100644 index 0000000000000..3019aa0ca27cf --- /dev/null +++ b/test/elf/X86_64/Inputs/externtls.x86-64 diff --git a/test/elf/X86_64/Inputs/fn.c b/test/elf/X86_64/Inputs/fn.c new file mode 100644 index 0000000000000..54939a2426b2e --- /dev/null +++ b/test/elf/X86_64/Inputs/fn.c @@ -0,0 +1,4 @@ +int fn() +{ + return 0; +} diff --git a/test/elf/X86_64/Inputs/fn.o b/test/elf/X86_64/Inputs/fn.o Binary files differnew file mode 100644 index 0000000000000..4b67d459dfdb2 --- /dev/null +++ b/test/elf/X86_64/Inputs/fn.o diff --git a/test/elf/X86_64/Inputs/generaltls-so.o.yaml b/test/elf/X86_64/Inputs/generaltls-so.o.yaml new file mode 100644 index 0000000000000..f0649e7639a6a --- /dev/null +++ b/test/elf/X86_64/Inputs/generaltls-so.o.yaml @@ -0,0 +1,68 @@ +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E566488D3D00000000666648E8000000008B005DC3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x0000000000000008 + Symbol: mynumber + Type: R_X86_64_TLSGD + Addend: -4 + - Offset: 0x0000000000000010 + Symbol: __tls_get_addr + Type: R_X86_64_PLT32 + Addend: -4 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 0x0000000000000004 + Content: '21000000' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .tdata + Type: STT_SECTION + Section: .tdata + Global: + - Name: getnumber + Type: STT_FUNC + Section: .text + Size: 0x0000000000000018 + - Name: mynumber + Type: STT_TLS + Section: .tdata + Size: 0x0000000000000004 + - Name: _GLOBAL_OFFSET_TABLE_ + - Name: __tls_get_addr +... diff --git a/test/elf/X86_64/Inputs/group/1.c b/test/elf/X86_64/Inputs/group/1.c new file mode 100644 index 0000000000000..f5e618bc4a855 --- /dev/null +++ b/test/elf/X86_64/Inputs/group/1.c @@ -0,0 +1,8 @@ +int _start() { + return 0; +} + +int main() { +fn(); +return 0; +} diff --git a/test/elf/X86_64/Inputs/group/1.o b/test/elf/X86_64/Inputs/group/1.o Binary files differnew file mode 100644 index 0000000000000..743518eb0fa3e --- /dev/null +++ b/test/elf/X86_64/Inputs/group/1.o diff --git a/test/elf/X86_64/Inputs/group/fn.c b/test/elf/X86_64/Inputs/group/fn.c new file mode 100644 index 0000000000000..e443c73a1ee05 --- /dev/null +++ b/test/elf/X86_64/Inputs/group/fn.c @@ -0,0 +1,4 @@ +int fn() { +fn1(); +return 0; +} diff --git a/test/elf/X86_64/Inputs/group/fn.o b/test/elf/X86_64/Inputs/group/fn.o Binary files differnew file mode 100644 index 0000000000000..1134432449cd6 --- /dev/null +++ b/test/elf/X86_64/Inputs/group/fn.o diff --git a/test/elf/X86_64/Inputs/group/fn1.c b/test/elf/X86_64/Inputs/group/fn1.c new file mode 100644 index 0000000000000..cca0df7fd2779 --- /dev/null +++ b/test/elf/X86_64/Inputs/group/fn1.c @@ -0,0 +1,3 @@ +int fn1() { +fn2(); +} diff --git a/test/elf/X86_64/Inputs/group/fn1.o b/test/elf/X86_64/Inputs/group/fn1.o Binary files differnew file mode 100644 index 0000000000000..2b02310003db8 --- /dev/null +++ b/test/elf/X86_64/Inputs/group/fn1.o diff --git a/test/elf/X86_64/Inputs/group/fn2.c b/test/elf/X86_64/Inputs/group/fn2.c new file mode 100644 index 0000000000000..a60370ed1e6b3 --- /dev/null +++ b/test/elf/X86_64/Inputs/group/fn2.c @@ -0,0 +1,3 @@ +int fn2() { +return 0; +} diff --git a/test/elf/X86_64/Inputs/group/fn2.o b/test/elf/X86_64/Inputs/group/fn2.o Binary files differnew file mode 100644 index 0000000000000..fabec849a775e --- /dev/null +++ b/test/elf/X86_64/Inputs/group/fn2.o diff --git a/test/elf/X86_64/Inputs/group/group.sh b/test/elf/X86_64/Inputs/group/group.sh new file mode 100755 index 0000000000000..2eba1030160be --- /dev/null +++ b/test/elf/X86_64/Inputs/group/group.sh @@ -0,0 +1,38 @@ +cat > 1.c << \! +int _start() { + return 0; +} + +int main() { +fn(); +return 0; +} +! + +cat > fn.c << \! +int fn() { +fn1(); +return 0; +} +! + +cat > fn2.c << \! +int fn2() { +return 0; +} +! + +cat > fn1.c << \! +int fn1() { +fn2(); +} +! + +gcc -c 1.c fn.c fn2.c fn1.c +ar cr libfn.a fn.o fn2.o +ar cr libfn1.a fn1.o +lld -flavor gnu -target x86_64 -shared -o libfn2.so fn2.o +lld -flavor gnu -target x86_64 1.o libfn.a libfn1.a -o x +lld -flavor gnu -target x86_64 1.o --start-group libfn.a libfn1.a --end-group -o x +lld -flavor gnu -target x86_64 1.o --start-group fn.o fn2.o fn1.o --end-group -o x +lld -flavor gnu -target x86_64 1.o --start-group --whole-archive libfn.a --no-whole-archive libfn1.a --end-group -o x diff --git a/test/elf/X86_64/Inputs/group/libfn.a b/test/elf/X86_64/Inputs/group/libfn.a Binary files differnew file mode 100644 index 0000000000000..c157c3babed9a --- /dev/null +++ b/test/elf/X86_64/Inputs/group/libfn.a diff --git a/test/elf/X86_64/Inputs/group/libfn.so b/test/elf/X86_64/Inputs/group/libfn.so Binary files differnew file mode 100755 index 0000000000000..fcbd11fe55199 --- /dev/null +++ b/test/elf/X86_64/Inputs/group/libfn.so diff --git a/test/elf/X86_64/Inputs/group/libfn1.a b/test/elf/X86_64/Inputs/group/libfn1.a Binary files differnew file mode 100644 index 0000000000000..69b9c75b5d168 --- /dev/null +++ b/test/elf/X86_64/Inputs/group/libfn1.a diff --git a/test/elf/X86_64/Inputs/group/libfn2.so b/test/elf/X86_64/Inputs/group/libfn2.so Binary files differnew file mode 100755 index 0000000000000..7ce867373910c --- /dev/null +++ b/test/elf/X86_64/Inputs/group/libfn2.so diff --git a/test/elf/X86_64/Inputs/initfini-option.c b/test/elf/X86_64/Inputs/initfini-option.c new file mode 100644 index 0000000000000..e9a6c08c12aca --- /dev/null +++ b/test/elf/X86_64/Inputs/initfini-option.c @@ -0,0 +1,13 @@ +#include <stdio.h> + +void init() { + printf("%s\n", __FUNCTION__); +} + +void fini() { + printf("%s\n", __FUNCTION__); +} + +int main() { +} + diff --git a/test/elf/X86_64/Inputs/initfini-option.o b/test/elf/X86_64/Inputs/initfini-option.o Binary files differnew file mode 100644 index 0000000000000..b1ba0557353c8 --- /dev/null +++ b/test/elf/X86_64/Inputs/initfini-option.o diff --git a/test/elf/X86_64/Inputs/initfini.c b/test/elf/X86_64/Inputs/initfini.c new file mode 100644 index 0000000000000..9427a86b6c9be --- /dev/null +++ b/test/elf/X86_64/Inputs/initfini.c @@ -0,0 +1,14 @@ +#include <stdio.h> + +void __attribute__ ((constructor)) constructor() { + printf("%s\n", __FUNCTION__); +} + +void __attribute__ ((destructor)) destructor() { + printf("%s\n", __FUNCTION__); +} + +int main() { + return 0; +} + diff --git a/test/elf/X86_64/Inputs/initfini.o b/test/elf/X86_64/Inputs/initfini.o Binary files differnew file mode 100644 index 0000000000000..f0e55a90b8b66 --- /dev/null +++ b/test/elf/X86_64/Inputs/initfini.o diff --git a/test/elf/X86_64/Inputs/largebss.c b/test/elf/X86_64/Inputs/largebss.c new file mode 100644 index 0000000000000..157d01755b6af --- /dev/null +++ b/test/elf/X86_64/Inputs/largebss.c @@ -0,0 +1,3 @@ +int largebss[1000] = { 0 }; +int largecommon[1000]; +__thread int largetbss[1000] = { 0 }; diff --git a/test/elf/X86_64/Inputs/largebss.o b/test/elf/X86_64/Inputs/largebss.o Binary files differnew file mode 100644 index 0000000000000..377370ec2db21 --- /dev/null +++ b/test/elf/X86_64/Inputs/largebss.o diff --git a/test/elf/X86_64/Inputs/layoutpass/1.c b/test/elf/X86_64/Inputs/layoutpass/1.c new file mode 100644 index 0000000000000..fec984503214f --- /dev/null +++ b/test/elf/X86_64/Inputs/layoutpass/1.c @@ -0,0 +1,8 @@ +int main() { + a(); + return 0; +} + +int b() { + return 0; +} diff --git a/test/elf/X86_64/Inputs/layoutpass/1.o b/test/elf/X86_64/Inputs/layoutpass/1.o Binary files differnew file mode 100644 index 0000000000000..848a5b3321ac6 --- /dev/null +++ b/test/elf/X86_64/Inputs/layoutpass/1.o diff --git a/test/elf/X86_64/Inputs/layoutpass/2.c b/test/elf/X86_64/Inputs/layoutpass/2.c new file mode 100644 index 0000000000000..fb9dbcc28aa6e --- /dev/null +++ b/test/elf/X86_64/Inputs/layoutpass/2.c @@ -0,0 +1,7 @@ +int a() { + return 0; +} + +int c() { + return 0; +} diff --git a/test/elf/X86_64/Inputs/layoutpass/2.o b/test/elf/X86_64/Inputs/layoutpass/2.o Binary files differnew file mode 100644 index 0000000000000..4c5ef8679581d --- /dev/null +++ b/test/elf/X86_64/Inputs/layoutpass/2.o diff --git a/test/elf/X86_64/Inputs/layoutpass/3.c b/test/elf/X86_64/Inputs/layoutpass/3.c new file mode 100644 index 0000000000000..054029fe5cfe8 --- /dev/null +++ b/test/elf/X86_64/Inputs/layoutpass/3.c @@ -0,0 +1,3 @@ +int d() { + return 0; +} diff --git a/test/elf/X86_64/Inputs/layoutpass/3.o b/test/elf/X86_64/Inputs/layoutpass/3.o Binary files differnew file mode 100644 index 0000000000000..76ef76e979019 --- /dev/null +++ b/test/elf/X86_64/Inputs/layoutpass/3.o diff --git a/test/elf/X86_64/Inputs/layoutpass/lib2.a b/test/elf/X86_64/Inputs/layoutpass/lib2.a Binary files differnew file mode 100644 index 0000000000000..5f076c869817a --- /dev/null +++ b/test/elf/X86_64/Inputs/layoutpass/lib2.a diff --git a/test/elf/X86_64/Inputs/libfn.a b/test/elf/X86_64/Inputs/libfn.a Binary files differnew file mode 100644 index 0000000000000..380844b0838da --- /dev/null +++ b/test/elf/X86_64/Inputs/libfn.a diff --git a/test/elf/X86_64/Inputs/libfn.so b/test/elf/X86_64/Inputs/libfn.so Binary files differnew file mode 100755 index 0000000000000..dc02480aa6902 --- /dev/null +++ b/test/elf/X86_64/Inputs/libfn.so diff --git a/test/elf/X86_64/Inputs/main.c b/test/elf/X86_64/Inputs/main.c new file mode 100644 index 0000000000000..0280c91270761 --- /dev/null +++ b/test/elf/X86_64/Inputs/main.c @@ -0,0 +1,4 @@ +int main() { + fn(); + return 0; +} diff --git a/test/elf/X86_64/Inputs/main.o b/test/elf/X86_64/Inputs/main.o Binary files differnew file mode 100644 index 0000000000000..ec8929f4b51df --- /dev/null +++ b/test/elf/X86_64/Inputs/main.o diff --git a/test/elf/X86_64/Inputs/multi-ovrd.c b/test/elf/X86_64/Inputs/multi-ovrd.c new file mode 100644 index 0000000000000..cf6c0b6ac3612 --- /dev/null +++ b/test/elf/X86_64/Inputs/multi-ovrd.c @@ -0,0 +1,10 @@ +#include <stdio.h> +void f(void) +{ + printf("overridden f!\n"); +} + +void g(void) +{ + printf("overridden g!\n"); +} diff --git a/test/elf/X86_64/Inputs/multi-ovrd.o b/test/elf/X86_64/Inputs/multi-ovrd.o Binary files differnew file mode 100644 index 0000000000000..e4c4d6037fdda --- /dev/null +++ b/test/elf/X86_64/Inputs/multi-ovrd.o diff --git a/test/elf/X86_64/Inputs/multi-weak.c b/test/elf/X86_64/Inputs/multi-weak.c new file mode 100644 index 0000000000000..10b9160352d99 --- /dev/null +++ b/test/elf/X86_64/Inputs/multi-weak.c @@ -0,0 +1,20 @@ +int fn() +{ + return 0; +} + +void __attribute__((weak)) f() +{ + printf("original f..\n"); +} + +void __attribute__((weak)) g() +{ + printf("original f..\n"); +} + +int main(void) +{ + f(); + return 0; +} diff --git a/test/elf/X86_64/Inputs/multi-weak.o b/test/elf/X86_64/Inputs/multi-weak.o Binary files differnew file mode 100644 index 0000000000000..d5677b699ac45 --- /dev/null +++ b/test/elf/X86_64/Inputs/multi-weak.o diff --git a/test/elf/X86_64/Inputs/multiweaksyms.o b/test/elf/X86_64/Inputs/multiweaksyms.o Binary files differnew file mode 100644 index 0000000000000..5e2e54d66b0d9 --- /dev/null +++ b/test/elf/X86_64/Inputs/multiweaksyms.o diff --git a/test/elf/X86_64/Inputs/nmagic.c b/test/elf/X86_64/Inputs/nmagic.c new file mode 100644 index 0000000000000..3ad15f0c49714 --- /dev/null +++ b/test/elf/X86_64/Inputs/nmagic.c @@ -0,0 +1,8 @@ +int a = 10; +__thread int b = 20; +__thread int c; +__thread int d; + +int main() { + return 0; +} diff --git a/test/elf/X86_64/Inputs/nmagic.o b/test/elf/X86_64/Inputs/nmagic.o Binary files differnew file mode 100644 index 0000000000000..af28e0ada8b33 --- /dev/null +++ b/test/elf/X86_64/Inputs/nmagic.o diff --git a/test/elf/X86_64/Inputs/no-interp-section.c b/test/elf/X86_64/Inputs/no-interp-section.c new file mode 100644 index 0000000000000..3981c038ed334 --- /dev/null +++ b/test/elf/X86_64/Inputs/no-interp-section.c @@ -0,0 +1 @@ +int c = 10; diff --git a/test/elf/X86_64/Inputs/no-interp-section.o b/test/elf/X86_64/Inputs/no-interp-section.o Binary files differnew file mode 100644 index 0000000000000..063eb3244f225 --- /dev/null +++ b/test/elf/X86_64/Inputs/no-interp-section.o diff --git a/test/elf/X86_64/Inputs/note.o b/test/elf/X86_64/Inputs/note.o Binary files differnew file mode 100644 index 0000000000000..d86b0cf85d192 --- /dev/null +++ b/test/elf/X86_64/Inputs/note.o diff --git a/test/elf/X86_64/Inputs/note.s b/test/elf/X86_64/Inputs/note.s new file mode 100644 index 0000000000000..0a0b03da6bb75 --- /dev/null +++ b/test/elf/X86_64/Inputs/note.s @@ -0,0 +1,11 @@ + .section ".note.ident", "a" + .p2align 2 + .long 1f - 0f # name size (not including padding) + .long 3f - 2f # desc size (not including padding) + .long 0x01234567 # type +0: .asciz "NaMe" # name +1: .p2align 2 +2: .long 0x76543210 # desc + .long 0x89abcdef +3: .p2align 2 + diff --git a/test/elf/X86_64/Inputs/note_ro_rw.o b/test/elf/X86_64/Inputs/note_ro_rw.o Binary files differnew file mode 100644 index 0000000000000..76f2486d2736c --- /dev/null +++ b/test/elf/X86_64/Inputs/note_ro_rw.o diff --git a/test/elf/X86_64/Inputs/note_ro_rw.s b/test/elf/X86_64/Inputs/note_ro_rw.s new file mode 100644 index 0000000000000..2a0eff5dfb2be --- /dev/null +++ b/test/elf/X86_64/Inputs/note_ro_rw.s @@ -0,0 +1,21 @@ + .section ".note.ro", "a" + .p2align 2 + .long 1f - 0f # name size (not including padding) + .long 3f - 2f # desc size (not including padding) + .long 0x01234567 # type +0: .asciz "NaMe" # name +1: .p2align 2 +2: .long 0x76543210 # desc + .long 0x89abcdef +3: .p2align 2 + .section ".note.rw", "aw" + .p2align 2 + .long 1f - 0f # name size (not including padding) + .long 3f - 2f # desc size (not including padding) + .long 0x01234567 # type +0: .asciz "NaMe" # name +1: .p2align 2 +2: .long 0x76543210 # desc + .long 0x89abcdef +3: .p2align 2 + diff --git a/test/elf/X86_64/Inputs/ovrd.c b/test/elf/X86_64/Inputs/ovrd.c new file mode 100644 index 0000000000000..a3d721dac2aa6 --- /dev/null +++ b/test/elf/X86_64/Inputs/ovrd.c @@ -0,0 +1,6 @@ +#include <stdio.h> +void f(void) +{ + printf("overridden f!\n"); +} + diff --git a/test/elf/X86_64/Inputs/ovrd.o b/test/elf/X86_64/Inputs/ovrd.o Binary files differnew file mode 100644 index 0000000000000..ea7353d94ed92 --- /dev/null +++ b/test/elf/X86_64/Inputs/ovrd.o diff --git a/test/elf/X86_64/Inputs/rodata.c b/test/elf/X86_64/Inputs/rodata.c new file mode 100644 index 0000000000000..01489fe550c80 --- /dev/null +++ b/test/elf/X86_64/Inputs/rodata.c @@ -0,0 +1,3 @@ +const char _nl_default_default_domain[] __attribute__ ((visibility ("hidden"))) = "messages"; +const char *_nl_current_default_domain __attribute__ ((visibility ("hidden"))) = _nl_default_default_domain; +const char _nl_default_default_dirname[] = "/usr/local"; diff --git a/test/elf/X86_64/Inputs/rodata.o b/test/elf/X86_64/Inputs/rodata.o Binary files differnew file mode 100644 index 0000000000000..660606cdc9c02 --- /dev/null +++ b/test/elf/X86_64/Inputs/rodata.o diff --git a/test/elf/X86_64/Inputs/rodata.s b/test/elf/X86_64/Inputs/rodata.s new file mode 100644 index 0000000000000..e1a2eb702736f --- /dev/null +++ b/test/elf/X86_64/Inputs/rodata.s @@ -0,0 +1,24 @@ + .file "x.c" + .hidden _nl_default_default_domain + .globl _nl_default_default_domain + .section .rodata._nl_default_default_domain,"ams",@progbits,1 + .type _nl_default_default_domain, @object + .size _nl_default_default_domain, 9 +_nl_default_default_domain: + .string "messages" + .hidden _nl_current_default_domain + .globl _nl_current_default_domain + .section .data._nl_current_default_domain,"aw",@progbits + .align 8 + .type _nl_current_default_domain, @object + .size _nl_current_default_domain, 8 +_nl_current_default_domain: + .quad _nl_default_default_domain + .globl _nl_default_default_dirname + .section .rodata._nl_default_default_dirname,"ams",@progbits,1 + .type _nl_default_default_dirname, @object + .size _nl_default_default_dirname, 11 +_nl_default_default_dirname: + .string "/usr/local" + .ident "GCC: (Ubuntu 4.8.1-2ubuntu1~10.04.1) 4.8.1" + .section .note.GNU-stack,"",@progbits diff --git a/test/elf/X86_64/Inputs/rwint.c b/test/elf/X86_64/Inputs/rwint.c new file mode 100644 index 0000000000000..d1cf7d62c801d --- /dev/null +++ b/test/elf/X86_64/Inputs/rwint.c @@ -0,0 +1 @@ +int a = 10; diff --git a/test/elf/X86_64/Inputs/rwint.o b/test/elf/X86_64/Inputs/rwint.o Binary files differnew file mode 100644 index 0000000000000..9fba51f07add2 --- /dev/null +++ b/test/elf/X86_64/Inputs/rwint.o diff --git a/test/elf/X86_64/Inputs/sectionmap.c b/test/elf/X86_64/Inputs/sectionmap.c new file mode 100644 index 0000000000000..c4f530148949f --- /dev/null +++ b/test/elf/X86_64/Inputs/sectionmap.c @@ -0,0 +1,4 @@ +int foo __attribute__((section(".gcc_except_table.foo"))) = 4; +const int bar __attribute__((section(".data.rel.local"))) = 2; +const int baz __attribute__((section(".data.rel.ro"))) = 2; +const int bak __attribute__((section(".data.xyz"))) = 2; diff --git a/test/elf/X86_64/Inputs/sectionmap.o b/test/elf/X86_64/Inputs/sectionmap.o Binary files differnew file mode 100644 index 0000000000000..599cf2cca5633 --- /dev/null +++ b/test/elf/X86_64/Inputs/sectionmap.o diff --git a/test/elf/X86_64/Inputs/undefcpp.c b/test/elf/X86_64/Inputs/undefcpp.c new file mode 100644 index 0000000000000..ce84c2a592b63 --- /dev/null +++ b/test/elf/X86_64/Inputs/undefcpp.c @@ -0,0 +1 @@ +int foo() { return _Z3fooPKc(); } diff --git a/test/elf/X86_64/Inputs/undefcpp.o b/test/elf/X86_64/Inputs/undefcpp.o Binary files differnew file mode 100644 index 0000000000000..6b8ebf5b6ab0c --- /dev/null +++ b/test/elf/X86_64/Inputs/undefcpp.o diff --git a/test/elf/X86_64/Inputs/weak-zero-sized.o b/test/elf/X86_64/Inputs/weak-zero-sized.o Binary files differnew file mode 100644 index 0000000000000..7c10c6a509ed1 --- /dev/null +++ b/test/elf/X86_64/Inputs/weak-zero-sized.o diff --git a/test/elf/X86_64/Inputs/weak.c b/test/elf/X86_64/Inputs/weak.c new file mode 100644 index 0000000000000..ae07ffc745d84 --- /dev/null +++ b/test/elf/X86_64/Inputs/weak.c @@ -0,0 +1,14 @@ +int fn() +{ + return 0; +} + +void __attribute__((weak)) f() +{ + printf("original f..\n"); +} +int main(void) +{ + f(); + return 0; +} diff --git a/test/elf/X86_64/Inputs/weak.o b/test/elf/X86_64/Inputs/weak.o Binary files differnew file mode 100644 index 0000000000000..41cc88a0c482a --- /dev/null +++ b/test/elf/X86_64/Inputs/weak.o diff --git a/test/elf/X86_64/Inputs/weak.s b/test/elf/X86_64/Inputs/weak.s new file mode 100644 index 0000000000000..64ce779553b62 --- /dev/null +++ b/test/elf/X86_64/Inputs/weak.s @@ -0,0 +1,21 @@ + .file "weak.s" + .text + .p2align 4,,15 + .globl test + .type test, @function +test: + ret + .size test, .-test + .weak myfn2 + .data + .align 8 + .type myfn2, @object + .size myfn2, 8 +myfn2: + .quad test + .weak myfn1 + .align 8 + .type myfn1, @object + .size myfn1, 8 +myfn1: + .quad test diff --git a/test/elf/X86_64/Inputs/zerosizedsection.o b/test/elf/X86_64/Inputs/zerosizedsection.o Binary files differnew file mode 100644 index 0000000000000..a0f2f13a307a5 --- /dev/null +++ b/test/elf/X86_64/Inputs/zerosizedsection.o diff --git a/test/elf/X86_64/Inputs/zerosizedsection.s b/test/elf/X86_64/Inputs/zerosizedsection.s new file mode 100644 index 0000000000000..651ee3aab5033 --- /dev/null +++ b/test/elf/X86_64/Inputs/zerosizedsection.s @@ -0,0 +1,3 @@ +.text +.data +.word .text diff --git a/test/elf/X86_64/alignoffset.test b/test/elf/X86_64/alignoffset.test new file mode 100644 index 0000000000000..b061f89379359 --- /dev/null +++ b/test/elf/X86_64/alignoffset.test @@ -0,0 +1,119 @@ +# Checks that segments are aligned as per ELF spec. The segment virtual address +# modulo page alignment should be equal to offset modulo page alignment. + +# Build executable +# RUN: yaml2obj -format=elf -docnum 1 %s -o %t.o +# RUN: lld -flavor gnu -target x86_64 %t.o -o %t.exe -static \ +# RUN: --no-align-segments --rosegment --noinhibit-exec +# RUN: llvm-readobj -program-headers %t.exe | FileCheck %s +# +#CHECK: Offset: 0x15C +#CHECK: VirtualAddress: 0x40015C +#CHECK: PhysicalAddress: 0x40015C +# +# +#const int a = 0; +#int main() { +# foo(); +# return 0; +#} +# +#int foo() { return 0; } + +# object +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E54883EC10C745FC00000000E81C000000B9000000008945F889C84883C4105DC36666662E0F1F840000000000554889E5B8000000005DC3 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .rodata + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '00000000' + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 005562756E747520636C616E672076657273696F6E20332E352E302D73766E3231373330342D317E6578703120286272616E636865732F72656C656173655F33352920286261736564206F6E204C4C564D20332E352E302900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: 1400000000000000037A5200017810011B0C070890010000180000001C000000000000002400000000410E108602430D060000001800000038000000000000000B00000000410E108602430D06000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .eh_frame + Relocations: + - Offset: 0x0000000000000020 + Symbol: .text + Type: R_X86_64_PC32 + Addend: 0 + - Offset: 0x000000000000003C + Symbol: .text + Type: R_X86_64_PC32 + Addend: 48 +Symbols: + Local: + - Name: 1.c + Type: STT_FILE + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .rodata + Type: STT_SECTION + Section: .rodata + - Name: .comment + Type: STT_SECTION + Section: .comment + - Name: .note.GNU-stack + Type: STT_SECTION + Section: .note.GNU-stack + - Name: .eh_frame + Type: STT_SECTION + Section: .eh_frame + Global: + - Name: a + Type: STT_OBJECT + Section: .rodata + Size: 0x0000000000000004 + - Name: foo + Type: STT_FUNC + Section: .text + Value: 0x0000000000000030 + Size: 0x000000000000000B + - Name: main + Type: STT_FUNC + Section: .text + Size: 0x0000000000000024 +... diff --git a/test/elf/X86_64/debug.test b/test/elf/X86_64/debug.test new file mode 100644 index 0000000000000..a13d12fba3828 --- /dev/null +++ b/test/elf/X86_64/debug.test @@ -0,0 +1,57 @@ +# Test that debug info is assigned typeNoAlloc and that the output sections have +# a virtual address of 0. +RUN: lld -flavor gnu -target x86_64 -e main --output-filetype=yaml \ +RUN: %p/Inputs/debug0.x86-64 %p/Inputs/debug1.x86-64 -o %t +RUN: FileCheck %s -check-prefix YAML < %t + +RUN: lld -flavor gnu -target x86_64 -e main %p/Inputs/debug0.x86-64 \ +RUN: %p/Inputs/debug1.x86-64 -o %t1 +RUN: llvm-readobj -sections %t1 | FileCheck %s -check-prefix ELF +# Verify that non SHF_ALLOC sections are relocated correctly. +RUN: llvm-objdump -s %t1 | FileCheck %s -check-prefix RELOC + +YAML: type: no-alloc + +ELF: Section { +ELF: Name: .debug_info +ELF: Type: SHT_PROGBITS (0x1) +ELF: Flags [ (0x0) +ELF: ] +ELF: Address: 0x0 +ELF: } +ELF: Section { +ELF: Name: .debug_abbrev +ELF: Type: SHT_PROGBITS (0x1) +ELF: Flags [ (0x0) +ELF: ] +ELF: Address: 0x0 +ELF: } +ELF: Section { +ELF: Name: .debug_aranges +ELF: Type: SHT_PROGBITS (0x1) +ELF: Flags [ (0x0) +ELF: ] +ELF: Address: 0x0 +ELF: } +ELF: Section { +ELF: Name: .debug_line +ELF: Type: SHT_PROGBITS (0x1) +ELF: Flags [ (0x0) +ELF: ] +ELF: Address: 0x0 +ELF: } +ELF: Section { +ELF: Name: .debug_str +ELF: Type: SHT_PROGBITS (0x1) +ELF: Flags [ (0x0) +ELF: ] +ELF: Address: 0x0 +ELF: } + +RELOC: Contents of section .debug_info: +RELOC: 0000 4e000000 04000000 00000801 3a000000 N...........:... +# ^^ Relocation: ._debug_str + 0x3a +RELOC: 0010 01780000 00000000 00dc0140 00000000 .x.........@.... +# ^^ Relocation: .debug_str + 0x78 +RELOC: 0020 00100000 00000000 00000000 00028100 ................ +# ^^ Relocation: .debug_str + 0x81 diff --git a/test/elf/X86_64/defsym.test b/test/elf/X86_64/defsym.test new file mode 100644 index 0000000000000..5d2256a09ac82 --- /dev/null +++ b/test/elf/X86_64/defsym.test @@ -0,0 +1,22 @@ +RUN: lld -flavor gnu -target x86_64 --defsym=main=fn --noinhibit-exec \ +RUN: %p/Inputs/fn.o -o %t +RUN: llvm-readobj -symbols %t | FileCheck %s + +CHECK: Symbol { +CHECK: Name: main (1) +CHECK: Value: 0x4001E0 +CHECK: Size: 0 +CHECK: Binding: Global (0x1) +CHECK: Type: Function (0x2) +CHECK: Other: 0 +CHECK: Section: .text (0x5) +CHECK: } +CHECK: Symbol { +CHECK: Name: fn (6) +CHECK: Value: 0x4001E0 +CHECK: Size: 6 +CHECK: Binding: Global (0x1) +CHECK: Type: Function (0x2) +CHECK: Other: 0 +CHECK: Section: .text (0x5) +CHECK: } diff --git a/test/elf/X86_64/demangle.test b/test/elf/X86_64/demangle.test new file mode 100644 index 0000000000000..1977fd7f1e3af --- /dev/null +++ b/test/elf/X86_64/demangle.test @@ -0,0 +1,12 @@ +# XFAIL: win32 +# +# Check that the linker is able to demangle strings properly. +# Once there is a way to add undefined symbols using yaml2obj, the test will be +# changed. + +RUN: lld -flavor gnu -target x86_64 %p/Inputs/undefcpp.o --noinhibit-exec 2>&1 | FileCheck -check-prefix=DEMANGLE %s +RUN: lld -flavor gnu -target x86_64 %p/Inputs/undefcpp.o --noinhibit-exec --no-demangle 2>&1 | FileCheck -check-prefix=NODEMANGLE %s +RUN: lld -flavor gnu -target x86_64 %p/Inputs/undefcpp.o --noinhibit-exec --demangle 2>&1 | FileCheck -check-prefix=DEMANGLE %s + +#DEMANGLE: undefcpp.o: foo(char const*) +#NODEMANGLE: undefcpp.o: _Z3fooPKc diff --git a/test/elf/X86_64/dontignorezerosize-sections.test b/test/elf/X86_64/dontignorezerosize-sections.test new file mode 100644 index 0000000000000..101e6cb55b244 --- /dev/null +++ b/test/elf/X86_64/dontignorezerosize-sections.test @@ -0,0 +1,9 @@ +# This tests that lld is not ignoring zero sized sections +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/zerosizedsection.o \ +RUN: --noinhibit-exec --output-filetype=yaml -o %t +RUN: FileCheck %s < %t + +CHECK: references: +CHECK: - kind: R_X86_64_16 +CHECK: offset: 0 +CHECK: target: L000 diff --git a/test/elf/X86_64/dynamicvars.test b/test/elf/X86_64/dynamicvars.test new file mode 100644 index 0000000000000..f5db73063650b --- /dev/null +++ b/test/elf/X86_64/dynamicvars.test @@ -0,0 +1,124 @@ +# Tests that the dynamic variables created by the linker are set to the right +# values. + +#RUN: yaml2obj --format elf -docnum 1 %s -o %t.o +#RUN: lld -flavor gnu -target x86_64 -e main %t.o -o %t1 --noinhibit-exec +#RUN: llvm-readobj -sections -symbols %t1 | FileCheck -check-prefix CHECKSYMS %s + + +#CHECKSYMS: Name: .dynamic +#CHECKSYMS: Type: SHT_DYNAMIC +#CHECKSYMS: Address: [[TARGETA:[0xa-fA-f0-9]+]] +#CHECKSYMS: Name: .got.plt +#CHECKSYMS: Type: SHT_PROGBITS +#CHECKSYMS: Address: [[TARGETB:[0xa-fA-f0-9]+]] +#CHECKSYMS: Name: _DYNAMIC +#CHECKSYMS: Value: [[TARGETA]] +#CHECKSYMS: Section: .dynamic +#CHECKSYMS: Name: _GLOBAL_OFFSET_TABLE_ +#CHECKSYMS: Value: [[TARGETB]] +#CHECKSYMS: Section: .got.plt + +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E54883EC10488B0500000000C745FC00000000C7000A000000E80000000031C98945F889C84883C4105DC36690554889E531C05DC3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x000000000000000B + Symbol: a + Type: R_X86_64_GOTPCREL + Addend: -4 + - Offset: 0x000000000000001D + Symbol: foo + Type: R_X86_64_PLT32 + Addend: -4 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 00636C616E672076657273696F6E20332E362E302000 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: 1400000000000000037A5200017810011B0C070890010000180000001C000000000000002E00000000410E108602430D060000001800000038000000000000000800000000410E108602430D06000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .eh_frame + Relocations: + - Offset: 0x0000000000000020 + Symbol: .text + Type: R_X86_64_PC32 + Addend: 0 + - Offset: 0x000000000000003C + Symbol: .text + Type: R_X86_64_PC32 + Addend: 48 +Symbols: + Local: + - Name: 1.c + Type: STT_FILE + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .comment + Type: STT_SECTION + Section: .comment + - Name: .note.GNU-stack + Type: STT_SECTION + Section: .note.GNU-stack + - Name: .eh_frame + Type: STT_SECTION + Section: .eh_frame + Global: + - Name: a + Type: STT_OBJECT + Value: 0x0000000000000004 + Size: 0x0000000000000004 + - Name: foo + Type: STT_FUNC + Section: .text + Value: 0x0000000000000030 + Size: 0x0000000000000008 + - Name: main + Type: STT_FUNC + Section: .text + Size: 0x000000000000002E + - Name: _GLOBAL_OFFSET_TABLE_ +... diff --git a/test/elf/X86_64/dynlib-nointerp-section.test b/test/elf/X86_64/dynlib-nointerp-section.test new file mode 100644 index 0000000000000..dca3d925b38be --- /dev/null +++ b/test/elf/X86_64/dynlib-nointerp-section.test @@ -0,0 +1,4 @@ +RUN: lld -flavor gnu -target x86_64 %p/Inputs/no-interp-section.o -o %t -shared +RUN: llvm-objdump -section-headers %t | FileCheck %s + +CHECK-NOT: .interp diff --git a/test/elf/X86_64/dynlib-search.test b/test/elf/X86_64/dynlib-search.test new file mode 100644 index 0000000000000..017208ac11ad3 --- /dev/null +++ b/test/elf/X86_64/dynlib-search.test @@ -0,0 +1,6 @@ +# This tests the functionality for finding the shared library libfn.so for ELF +RUN: lld -flavor gnu -target x86_64 %p/Inputs/main.o -L%p/Inputs/ -lfn -o %t \ +RUN: --noinhibit-exec -t 2> %t1 +RUN: FileCheck %s < %t1 + +CHECK: {{[\/0-9A-Za-z_]+}}libfn.so diff --git a/test/elf/X86_64/dynsym-weak.test b/test/elf/X86_64/dynsym-weak.test new file mode 100644 index 0000000000000..4f05656a41f2a --- /dev/null +++ b/test/elf/X86_64/dynsym-weak.test @@ -0,0 +1,118 @@ +# Check that a symbol declared as a week in a shared library gets a dynamic +# symbol table record in an executable file if this executabe file declares the +# symbol as strong. + +# RUN: yaml2obj -format=elf -docnum 1 %s > %t.foo.o +# RUN: lld -flavor gnu -target x86_64 -shared -o %t.so %t.foo.o +# RUN: yaml2obj -format=elf -docnum 2 %s > %t.main.o +# +# Link executable file with strong symbol. Weak symbol is in the shared lib. +# RUN: lld -flavor gnu -target x86_64 -e main -o %t1.exe %t.main.o %t.so +# RUN: llvm-readobj -dyn-symbols %t1.exe | FileCheck -check-prefix=EXE %s +# +# Link executable file. Strong and weak symbol come from different object files. +# RUN: lld -flavor gnu -target x86_64 -e main -o %t2.exe %t.main.o %t.foo.o +# RUN: llvm-readobj -dyn-symbols %t2.exe | FileCheck -check-prefix=OBJ %s +# +# Link shared library. Weak symbol is in the another shared lib. +# RUN: lld -flavor gnu -target x86_64 -shared -o %t.res.so %t.main.o %t.so +# RUN: llvm-readobj -dyn-symbols %t.res.so | FileCheck -check-prefix=SO %s + +# EXE: Symbol { +# EXE: Name: flag@ ({{[0-9]+}}) +# EXE-NEXT: Value: 0x{{[0-9A-F]+}} +# EXE-NEXT: Size: 4 +# EXE-NEXT: Binding: Global (0x1) +# EXE-NEXT: Type: Object (0x1) +# EXE-NEXT: Other: 0 +# EXE-NEXT: Section: .data (0x{{[0-9A-F]+}}) +# EXE-NEXT: } + +# OBJ-NOT: Name: flag@ ({{[0-9]+}}) + +# SO: Symbol { +# SO: Name: flag@ ({{[0-9]+}}) +# SO-NEXT: Value: 0x{{[0-9A-F]+}} +# SO-NEXT: Size: 4 +# SO-NEXT: Binding: Global (0x1) +# SO-NEXT: Type: Object (0x1) +# SO-NEXT: Other: 0 +# SO-NEXT: Section: .data (0x{{[0-9A-F]+}}) +# SO-NEXT: } + +# foo.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x08 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x08 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: flag + Type: R_X86_64_GOTPCREL + Addend: -4 + +Symbols: + Global: + - Name: foo + Type: STT_FUNC + Section: .text + Size: 0x08 + Weak: + - Name: flag + +# main.o +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x08 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x08 + Info: .text + Relocations: + - Offset: 0x00 + Symbol: foo + Type: R_X86_64_PLT32 + Addend: -4 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x04 + Size: 0x04 + +Symbols: + Global: + - Name: flag + Type: STT_OBJECT + Section: .data + Size: 0x04 + - Name: main + Type: STT_FUNC + Section: .text + Size: 0x08 + - Name: foo +... diff --git a/test/elf/X86_64/extern-tls.test b/test/elf/X86_64/extern-tls.test new file mode 100644 index 0000000000000..c8e7580e5f3c9 --- /dev/null +++ b/test/elf/X86_64/extern-tls.test @@ -0,0 +1,16 @@ +# This tests verifies that TLS variables have correct offsets +# when variables the TLS variables are not defined in the program +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/externtls.x86-64 -static \ +RUN: --output-filetype=yaml --noinhibit-exec | FileCheck %s -check-prefix=CHECKGOT + + - name: __got_tls_extern_tls +CHECKGOT: type: got +CHECKGOT: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ] +CHECKGOT: alignment: 2^3 +CHECKGOT: section-choice: custom-required +CHECKGOT: section-name: .got +CHECKGOT: permissions: rw- +CHECKGOT: references: +CHECKGOT: - kind: R_X86_64_TPOFF64 +CHECKGOT: offset: 0 +CHECKGOT: target: extern_tls diff --git a/test/elf/X86_64/general-dynamic-tls.test b/test/elf/X86_64/general-dynamic-tls.test new file mode 100644 index 0000000000000..c1a6f6e5d1201 --- /dev/null +++ b/test/elf/X86_64/general-dynamic-tls.test @@ -0,0 +1,129 @@ +# This test exercises a simple general dynamic TLS access model in X86_64. +# +# It is composed of two parts: a program and a shared library. The shared +# library uses TLS, but the program does not. +# +# The shared library should import __tls_get_addr, since it uses the general +# dynamic TLS access mode (see www.akkadia.org/drepper/tls.pdf). Notice that +# once we support TLS strength reduction, this test should be updated, since +# this can be converted into a local dynamic TLS model. + +# Prepare inputs +#RUN: yaml2obj -format=elf %p/Inputs/generaltls-so.o.yaml -o=%t.o.so +#RUN: lld -flavor gnu -target x86_64 -shared %t.o.so -o %T/libgeneraltls.so +#RUN: yaml2obj -format=elf %s -o=%t.o + +# Link - (we supply --defsym=__tls_get_addr to avoid the need to link with +# system libraries) +#RUN: lld -flavor gnu -target x86_64 -e main %t.o -L%T -lgeneraltls -o %t1 \ +#RUN: --defsym=__tls_get_addr=0 + +# Check +#RUN: llvm-readobj -dyn-symbols %t1 | FileCheck -check-prefix CHECKPROG %s +#RUN: llvm-readobj -relocations -dyn-symbols %T/libgeneraltls.so | FileCheck \ +#RUN: -check-prefix CHECKDSO %s + +# Test case generated with the following code: +# +# DSO: (file %p/Inputs/generaltls-so.o.yaml) +# +# __thread int mynumber=33; +# +# int getnumber() { +# return mynumber; +# } +# +# Program: (this file). Note: The printf() relocation was removed to simplify +# this test and allow us to test this without libc. +# +# #include <stdio.h> +# int getnumber(); +# +# int main() { +# printf("getnumber() = %d\n", getnumber()); +# return 0; +# } +# +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E54883EC10C745FC00000000B000E80000000048BF000000000000000089C6B000E80000000031F68945F889F04883C4105DC3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x0000000000000012 + Symbol: getnumber + Type: R_X86_64_PC32 + Addend: -4 + - Offset: 0x0000000000000018 + Symbol: .rodata.str1.1 + Type: R_X86_64_64 + Addend: 0 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .rodata.str1.1 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 6765746E756D6265722829203D2025640A00 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .rodata.str1.1 + Type: STT_SECTION + Section: .rodata.str1.1 + Global: + - Name: main + Type: STT_FUNC + Section: .text + Size: 0x0000000000000036 + - Name: getnumber + +# Program should import the function defined in the shared library +#CHECKPROG: getnumber@ +# Program should not import __tls_get_addr, since it does not directly use TLS +#CHECKPROG-NOT: __tls_get_addr@ + +# Check for the presence of X86_64 TLS relocations in the shared library +#CHECKDSO: R_X86_64_DTPMOD64 +#CHECKDSO: R_X86_64_DTPOFF64 +#CHECKDSO: R_X86_64_JUMP_SLOT + +# The shared library should import __tls_get_addr, since it uses the general +# dynamic TLS access mode. +#CHECKDSO: Name: __tls_get_addr@ +#CHECKDSO-NEXT: Value: 0x0 +#CHECKDSO-NEXT: Size: 0 +#CHECKDSO-NEXT: Binding: Global +#CHECKDSO-NEXT: Type: None +#CHECKDSO-NEXT: Other: 0 +#CHECKDSO-NEXT: Section: Undefined + diff --git a/test/elf/X86_64/imagebase.test b/test/elf/X86_64/imagebase.test new file mode 100644 index 0000000000000..67c3b6e1ff496 --- /dev/null +++ b/test/elf/X86_64/imagebase.test @@ -0,0 +1,94 @@ +# Checks that segments start at the image address specified. + +# Build executable +# RUN: yaml2obj -format=elf -docnum 1 %s -o %t.o +# RUN: lld -flavor gnu -target x86_64 %t.o -o %t.exe -static \ +# RUN: --no-align-segments --noinhibit-exec --image-base 0x600000 +# RUN: llvm-readobj -program-headers %t.exe | FileCheck %s +# +#CHECK: VirtualAddress: 0x600000 +#CHECK: PhysicalAddress: 0x600000 +#CHECK: VirtualAddress: 0x600178 +#CHECK: PhysicalAddress: 0x600178 + +# object +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E5B864000000C745FC000000005DC366666666662E0F1F840000000000554889E531C05DC3 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '64000000' + - Name: .rodata + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '64000000' + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: 1400000000000000037A5200017810011B0C070890010000180000001C000000000000001200000000410E108602430D060000001800000038000000000000000800000000410E108602430D06000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .eh_frame + Relocations: + - Offset: 0x0000000000000020 + Symbol: .text + Type: R_X86_64_PC32 + Addend: 0 + - Offset: 0x000000000000003C + Symbol: .text + Type: R_X86_64_PC32 + Addend: 32 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .eh_frame + Type: STT_SECTION + Section: .eh_frame + Global: + - Name: foo + Type: STT_FUNC + Section: .text + Value: 0x0000000000000020 + Size: 0x0000000000000008 + - Name: main + Type: STT_FUNC + Section: .text + Size: 0x0000000000000012 + - Name: myval + Type: STT_OBJECT + Section: .bss + Size: 0x0000000000000004 + - Name: val + Type: STT_OBJECT + Section: .rodata + Size: 0x0000000000000004 +... diff --git a/test/elf/X86_64/initfini-order.test b/test/elf/X86_64/initfini-order.test new file mode 100644 index 0000000000000..d3981eb0914e9 --- /dev/null +++ b/test/elf/X86_64/initfini-order.test @@ -0,0 +1,10 @@ +# This tests the functionality that lld is able to emit +# init_array/fini_array sections in the right order. + +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/initfini.o \ +RUN: --noinhibit-exec -o %t +RUN: llvm-objdump -t -section-headers %t | FileCheck %s + +CHECK: {{[0-9]+}} .eh_frame {{[0-9a-z]+}} {{[0-9a-z]+}} DATA +CHECK: {{[0-9]+}} .init_array {{[0-9a-z]+}} {{[0-9a-z]+}} DATA +CHECK: {{[0-9]+}} .fini_array {{[0-9a-z]+}} {{[0-9a-z]+}} DATA diff --git a/test/elf/X86_64/initfini.test b/test/elf/X86_64/initfini.test new file mode 100644 index 0000000000000..d882352a1c384 --- /dev/null +++ b/test/elf/X86_64/initfini.test @@ -0,0 +1,23 @@ +# This tests the functionality that lld is able to read +# init_array/fini_array sections in the input ELF. This +# corresponds to the the .init_array/.fini_array sections +# in the output ELF. + +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/initfini.o \ +RUN: --noinhibit-exec --output-filetype=yaml -o %t +RUN: FileCheck %s < %t + +CHECK: - type: data +CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ] +CHECK: section-name: .init_array +CHECK: references: +CHECK: - kind: R_X86_64_64 +CHECK: offset: 0 +CHECK: target: constructor +CHECK: - type: data +CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ] +CHECK: section-name: .fini_array +CHECK: references: +CHECK: - kind: R_X86_64_64 +CHECK: offset: 0 +CHECK: target: destructor diff --git a/test/elf/X86_64/largebss.test b/test/elf/X86_64/largebss.test new file mode 100644 index 0000000000000..d2dde4954139e --- /dev/null +++ b/test/elf/X86_64/largebss.test @@ -0,0 +1,20 @@ +# This tests the functionality of handling BSS symbols +# BSS symbols don't occupy file content and are associated with typeZeroFill +# Any typeZeroFill content wouldn't have space reserved in the file to store +# its content + +RUN: lld -flavor gnu -target x86_64 %p/Inputs/largebss.o --output-filetype=yaml --noinhibit-exec | FileCheck %s + +CHECK: - name: largecommon +CHECK: scope: global +CHECK: type: zero-fill +CHECK: size: 4000 +CHECK: merge: as-tentative +CHECK: - name: largebss +CHECK: scope: global +CHECK: type: zero-fill +CHECK: size: 4000 +CHECK: - name: largetbss +CHECK: scope: global +CHECK: type: thread-zero-fill +CHECK: size: 4000 diff --git a/test/elf/X86_64/layoutpass-order.test b/test/elf/X86_64/layoutpass-order.test new file mode 100644 index 0000000000000..e4ebef1d52db0 --- /dev/null +++ b/test/elf/X86_64/layoutpass-order.test @@ -0,0 +1,14 @@ +# This test checks that we follow the command line order of layouting +# symbols in the output file + +RUN: lld -flavor gnu -target x86_64 %p/Inputs/layoutpass/1.o \ +RUN: %p/Inputs/layoutpass/lib2.a %p/Inputs/layoutpass/3.o -o %t \ +RUN: --noinhibit-exec -static + +RUN: llvm-nm -n %t | FileCheck -check-prefix=SYMBOLSORDER %s + +SYMBOLSORDER: {{[A-Fa-f0-9]+}} T main +SYMBOLSORDER: {{[A-Fa-f0-9]+}} T b +SYMBOLSORDER: {{[A-Fa-f0-9]+}} T a +SYMBOLSORDER: {{[A-Fa-f0-9]+}} T c +SYMBOLSORDER: {{[A-Fa-f0-9]+}} T d diff --git a/test/elf/X86_64/maxpagesize.test b/test/elf/X86_64/maxpagesize.test new file mode 100644 index 0000000000000..649d09fcada02 --- /dev/null +++ b/test/elf/X86_64/maxpagesize.test @@ -0,0 +1,113 @@ +# Checks that segments are aligned as per ELF spec when the user specifies +# max-page-size option, and the segment alignment is set to the page size +# specified by the user. + +# Build executable +# RUN: yaml2obj -format=elf -docnum 1 %s -o %t.o +# RUN: not lld -flavor gnu -target x86_64 %t.o -o %t.exe -static \ +# RUN: --no-align-segments --noinhibit-exec -z max-page-size=0 +# RUN: not lld -flavor gnu -target x86_64 %t.o -o %t.exe -static \ +# RUN: --no-align-segments --noinhibit-exec -z max-page-size=0xFF +# RUN: not lld -flavor gnu -target x86_64 %t.o -o %t.exe -static \ +# RUN: --no-align-segments --noinhibit-exec -z max-page-size=0x1010 +# RUN: lld -flavor gnu -target x86_64 %t.o -o %t1.exe -static \ +# RUN: --no-align-segments --noinhibit-exec -z max-page-size=0x100000 +# RUN: lld -flavor gnu -target x86_64 %t.o -o %t.exe -static \ +# RUN: --no-align-segments --noinhibit-exec -z max-page-size=0x10000 +# RUN: llvm-readobj -program-headers %t.exe | FileCheck %s +# RUN: llvm-readobj -program-headers %t1.exe | FileCheck %s -check-prefix=CHECKLARGE +# +#CHECK: VirtualAddress: 0x400000 +#CHECK: PhysicalAddress: 0x400000 +#CHECK: Alignment: 65536 +#CHECK: VirtualAddress: 0x400178 +#CHECK: PhysicalAddress: 0x400178 +#CHECK: Alignment: 65536 +#CHECKLARGE: VirtualAddress: 0x400000 +#CHECKLARGE: PhysicalAddress: 0x400000 +#CHECKLARGE: Alignment: 1048576 +#CHECKLARGE: VirtualAddress: 0x400178 +#CHECKLARGE: PhysicalAddress: 0x400178 +#CHECKLARGE: Alignment: 1048576 + +# object +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E5B864000000C745FC000000005DC366666666662E0F1F840000000000554889E531C05DC3 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '64000000' + - Name: .rodata + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '64000000' + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: 1400000000000000037A5200017810011B0C070890010000180000001C000000000000001200000000410E108602430D060000001800000038000000000000000800000000410E108602430D06000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .eh_frame + Relocations: + - Offset: 0x0000000000000020 + Symbol: .text + Type: R_X86_64_PC32 + Addend: 0 + - Offset: 0x000000000000003C + Symbol: .text + Type: R_X86_64_PC32 + Addend: 32 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .eh_frame + Type: STT_SECTION + Section: .eh_frame + Global: + - Name: foo + Type: STT_FUNC + Section: .text + Value: 0x0000000000000020 + Size: 0x0000000000000008 + - Name: main + Type: STT_FUNC + Section: .text + Size: 0x0000000000000012 + - Name: myval + Type: STT_OBJECT + Section: .bss + Size: 0x0000000000000004 + - Name: val + Type: STT_OBJECT + Section: .rodata + Size: 0x0000000000000004 +... diff --git a/test/elf/X86_64/mergesimilarstrings.test b/test/elf/X86_64/mergesimilarstrings.test new file mode 100644 index 0000000000000..3836f0b507589 --- /dev/null +++ b/test/elf/X86_64/mergesimilarstrings.test @@ -0,0 +1,47 @@ +# Check that relocations to section that contains strings is properly handled +# when merging strings is enabled. +# +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target x86_64 %t.o --noinhibit-exec -o %t1.out +# RUN: llvm-readobj -sections %t1.out | FileCheck %s +# RUN: lld -flavor gnu -target x86_64 %t.o --noinhibit-exec -o %t2.out --output-filetype=yaml +# RUN: FileCheck %s -check-prefix=CHECKRELOCS < %t2.out + +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Content: 54889e5488d3d00000000e80000000088d3d00000000e800000000b8000000005dc3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x04 + Info: .text + Relocations: + - Offset: 0x07 + Symbol: .rodata + Type: R_X86_64_PC32 + Addend: -4 + - Name: .rodata + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x01 + Content: 48656c6c6f20576f726c6400576f726c6400 +Symbols: + Global: + - Name: .rodata + Section: .rodata + +#CHECK: Name: .rodata +#CHECK: Size: 18 +#CHECKRELOCS: references: +#CHECKRELOCS: - kind: R_X86_64_PC32 +#CHECKRELOCS: offset: 7 +#CHECKRELOCS: target: .rodata +#CHECKRELOCS: addend: -4 diff --git a/test/elf/X86_64/multi-weak-layout.test b/test/elf/X86_64/multi-weak-layout.test new file mode 100644 index 0000000000000..4bbf1dfc7e904 --- /dev/null +++ b/test/elf/X86_64/multi-weak-layout.test @@ -0,0 +1,52 @@ +# Test that we are able to layout multiple weak symbols +# properly + +RUN: lld -flavor gnu -target x86_64 %p/Inputs/multiweaksyms.o \ +RUN: --noinhibit-exec -static --output-filetype=yaml -o %t +RUN: FileCheck %s -check-prefix=WEAKSYMS < %t + +WEAKSYMS: - type: data +WEAKSYMS: alignment: 2^3 +WEAKSYMS: references: +WEAKSYMS: - kind: layout-after +WEAKSYMS: offset: 0 +WEAKSYMS: target: [[L001:[-a-zA-Z0-9_]+]] +WEAKSYMS: - name: myfn2 +WEAKSYMS: scope: global +WEAKSYMS: type: data +WEAKSYMS: merge: as-weak +WEAKSYMS: alignment: 2^3 +WEAKSYMS: references: +WEAKSYMS: - kind: layout-after +WEAKSYMS: offset: 0 +WEAKSYMS: target: [[L001]] +WEAKSYMS: - ref-name: [[L001]] +WEAKSYMS: scope: global +WEAKSYMS: type: data +WEAKSYMS: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ] +WEAKSYMS: alignment: 2^3 +WEAKSYMS: references: +WEAKSYMS: - kind: R_X86_64_64 +WEAKSYMS: offset: 0 +WEAKSYMS: target: test +WEAKSYMS: - kind: layout-after +WEAKSYMS: offset: 0 +WEAKSYMS: target: [[L003:[-a-zA-Z0-9_]+]] +WEAKSYMS: - name: myfn1 +WEAKSYMS: scope: global +WEAKSYMS: type: data +WEAKSYMS: merge: as-weak +WEAKSYMS: alignment: 2^3 +WEAKSYMS: references: +WEAKSYMS: - kind: layout-after +WEAKSYMS: offset: 0 +WEAKSYMS: target: [[L003]] +WEAKSYMS: - ref-name: [[L003]] +WEAKSYMS: scope: global +WEAKSYMS: type: data +WEAKSYMS: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ] +WEAKSYMS: alignment: 2^3 +WEAKSYMS: references: +WEAKSYMS: - kind: R_X86_64_64 +WEAKSYMS: offset: 0 +WEAKSYMS: target: test diff --git a/test/elf/X86_64/multi-weak-override.test b/test/elf/X86_64/multi-weak-override.test new file mode 100644 index 0000000000000..f2d0e0c2f77b7 --- /dev/null +++ b/test/elf/X86_64/multi-weak-override.test @@ -0,0 +1,16 @@ +# Test for weak symbol getting overridden +RUN: lld -flavor gnu -target x86_64 %p/Inputs/multi-weak.o \ +RUN: %p/Inputs/multi-ovrd.o -o %t -e main --noinhibit-exec +RUN: llvm-nm -n %t | FileCheck -check-prefix=WEAKORDER %s +RUN: lld -flavor gnu -target x86_64 %p/Inputs/multi-weak.o \ +RUN: %p/Inputs/multi-ovrd.o --output-filetype=yaml -o %t2 --noinhibit-exec +RUN: FileCheck -check-prefix=WEAKATOMSORDER %s < %t2 + +WEAKORDER: {{[0-9a-f]+}} T f +WEAKORDER: {{[0-9a-f]+}} T g + +WEAKATOMSORDER: - ref-name: {{[A-Z0-9]+}} +WEAKATOMSORDER: - ref-name: {{[A-Z0-9]+}} +WEAKATOMSORDER: - name: f +WEAKATOMSORDER: - name: g + diff --git a/test/elf/X86_64/multi-weak-syms-order.test b/test/elf/X86_64/multi-weak-syms-order.test new file mode 100644 index 0000000000000..2b414593fed41 --- /dev/null +++ b/test/elf/X86_64/multi-weak-syms-order.test @@ -0,0 +1,13 @@ +# Test for weak symbol getting overridden +RUN: lld -flavor gnu -target x86_64 %p/Inputs/multi-weak.o -o %t --noinhibit-exec +RUN: llvm-nm -n %t | FileCheck -check-prefix=WEAKORDER %s +RUN: lld -flavor gnu -target x86_64 %p/Inputs/multi-weak.o -o %t2 --output-filetype=yaml --noinhibit-exec +RUN: FileCheck -check-prefix=WEAKATOMSORDER %s < %t2 + +WEAKORDER: {{[0-9a-f]+}} T fn +WEAKORDER: {{[0-9a-f]+}} T f +WEAKORDER: {{[0-9a-f]+}} T g +WEAKORDER: {{[0-9a-f]+}} T main + +WEAKATOMSORDER: - name: f +WEAKATOMSORDER: - name: g diff --git a/test/elf/X86_64/nmagic.test b/test/elf/X86_64/nmagic.test new file mode 100644 index 0000000000000..b313c1f051ba6 --- /dev/null +++ b/test/elf/X86_64/nmagic.test @@ -0,0 +1,91 @@ +# This tests verifies functionality of NMAGIC that we create only two segments, +# PT_LOAD, PT_TLS +# The data segment should be aligned to a page boundary +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/nmagic.o \ +RUN: --noinhibit-exec -o %t --nmagic -static +RUN: llvm-readobj -sections %t | FileCheck -check-prefix=NMAGICSECTIONS %s +RUN: llvm-readobj -program-headers %t | FileCheck -check-prefix=NMAGICPROGRAMHEADERS %s + +NMAGICSECTIONS: Section { +NMAGICSECTIONS: Index: 0 +NMAGICSECTIONS: Name: (0) +NMAGICSECTIONS: Type: SHT_NULL (0x0) +NMAGICSECTIONS: } +NMAGICSECTIONS: Section { +NMAGICSECTIONS: Name: .text +NMAGICSECTIONS: Type: SHT_PROGBITS (0x1) +NMAGICSECTIONS: } +NMAGICSECTIONS: Section { +NMAGICSECTIONS: Name: .eh_frame +NMAGICSECTIONS: Type: SHT_PROGBITS (0x1) +NMAGICSECTIONS: } +NMAGICSECTIONS: Section { +NMAGICSECTIONS: Name: .tdata +NMAGICSECTIONS: Type: SHT_PROGBITS (0x1) +NMAGICSECTIONS: } +NMAGICSECTIONS: Section { +NMAGICSECTIONS: Name: .tbss +NMAGICSECTIONS: Type: SHT_NOBITS (0x8) +NMAGICSECTIONS: } +NMAGICSECTIONS: Section { +NMAGICSECTIONS: Name: .got.plt +NMAGICSECTIONS: Type: SHT_PROGBITS (0x1) +NMAGICSECTIONS: } +NMAGICSECTIONS: Section { +NMAGICSECTIONS: Name: .data +NMAGICSECTIONS: Type: SHT_PROGBITS (0x1) +NMAGICSECTIONS: } +NMAGICSECTIONS: Section { +NMAGICSECTIONS: Name: .bss +NMAGICSECTIONS: Type: SHT_NOBITS (0x8) +NMAGICSECTIONS: } +NMAGICSECTIONS: Section { +NMAGICSECTIONS: Name: .comment +NMAGICSECTIONS: Type: SHT_PROGBITS (0x1) +NMAGICSECTIONS: } +NMAGICSECTIONS: Section { +NMAGICSECTIONS: Name: .note.GNU-stack +NMAGICSECTIONS: Type: SHT_PROGBITS (0x1) +NMAGICSECTIONS: } +NMAGICSECTIONS: Section { +NMAGICSECTIONS: Name: .shstrtab +NMAGICSECTIONS: Type: SHT_STRTAB (0x3) +NMAGICSECTIONS: } +NMAGICSECTIONS: Section { +NMAGICSECTIONS: Name: .symtab +NMAGICSECTIONS: Type: SHT_SYMTAB (0x2) +NMAGICSECTIONS: } +NMAGICSECTIONS: Section { +NMAGICSECTIONS: Name: .strtab +NMAGICSECTIONS: Type: SHT_STRTAB (0x3) +NMAGICSECTIONS: } + +NMAGICPROGRAMHEADERS: ProgramHeaders [ +NMAGICPROGRAMHEADERS: ProgramHeader { +NMAGICPROGRAMHEADERS: Type: PT_LOAD (0x1) +NMAGICPROGRAMHEADERS: Offset: 0x0 +NMAGICPROGRAMHEADERS: VirtualAddress: 0x400000 +NMAGICPROGRAMHEADERS: PhysicalAddress: 0x400000 +NMAGICPROGRAMHEADERS: FileSize: 4108 +NMAGICPROGRAMHEADERS: MemSize: 4108 +NMAGICPROGRAMHEADERS: Flags [ (0x7) +NMAGICPROGRAMHEADERS: PF_R (0x4) +NMAGICPROGRAMHEADERS: PF_W (0x2) +NMAGICPROGRAMHEADERS: PF_X (0x1) +NMAGICPROGRAMHEADERS: ] +NMAGICPROGRAMHEADERS: Alignment: 8 +NMAGICPROGRAMHEADERS: } +NMAGICPROGRAMHEADERS: ProgramHeader { +NMAGICPROGRAMHEADERS: Type: PT_TLS (0x7) +NMAGICPROGRAMHEADERS: Offset: 0x1000 +NMAGICPROGRAMHEADERS: VirtualAddress: 0x401000 +NMAGICPROGRAMHEADERS: PhysicalAddress: 0x401000 +NMAGICPROGRAMHEADERS: FileSize: 4 +NMAGICPROGRAMHEADERS: MemSize: 12 +NMAGICPROGRAMHEADERS: Flags [ (0x6) +NMAGICPROGRAMHEADERS: PF_R (0x4) +NMAGICPROGRAMHEADERS: PF_W (0x2) +NMAGICPROGRAMHEADERS: ] +NMAGICPROGRAMHEADERS: Alignment: 4 +NMAGICPROGRAMHEADERS: } +NMAGICPROGRAMHEADERS: ] diff --git a/test/elf/X86_64/noalignsegments.test b/test/elf/X86_64/noalignsegments.test new file mode 100644 index 0000000000000..8432bda51f005 --- /dev/null +++ b/test/elf/X86_64/noalignsegments.test @@ -0,0 +1,95 @@ +# Checks that segments are aligned as per ELF spec than aligning each +# segment fileoffset / virtual address to a page. + +# Build executable +# RUN: yaml2obj -format=elf -docnum 1 %s -o %t.o +# RUN: lld -flavor gnu -target x86_64 %t.o -o %t.exe -static \ +# RUN: --no-align-segments --noinhibit-exec +# RUN: llvm-readobj -program-headers %t.exe | FileCheck %s +# +#CHECK: VirtualAddress: 0x400000 +#CHECK: PhysicalAddress: 0x400000 +#CHECK: VirtualAddress: 0x400178 +#CHECK: PhysicalAddress: 0x400178 + +# object +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E5B864000000C745FC000000005DC366666666662E0F1F840000000000554889E531C05DC3 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '64000000' + - Name: .rodata + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '64000000' + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: 1400000000000000037A5200017810011B0C070890010000180000001C000000000000001200000000410E108602430D060000001800000038000000000000000800000000410E108602430D06000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .eh_frame + Relocations: + - Offset: 0x0000000000000020 + Symbol: .text + Type: R_X86_64_PC32 + Addend: 0 + - Offset: 0x000000000000003C + Symbol: .text + Type: R_X86_64_PC32 + Addend: 32 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .eh_frame + Type: STT_SECTION + Section: .eh_frame + Global: + - Name: foo + Type: STT_FUNC + Section: .text + Value: 0x0000000000000020 + Size: 0x0000000000000008 + - Name: main + Type: STT_FUNC + Section: .text + Size: 0x0000000000000012 + - Name: myval + Type: STT_OBJECT + Section: .bss + Size: 0x0000000000000004 + - Name: val + Type: STT_OBJECT + Section: .rodata + Size: 0x0000000000000004 +... diff --git a/test/elf/X86_64/note-sections-ro_plus_rw.test b/test/elf/X86_64/note-sections-ro_plus_rw.test new file mode 100644 index 0000000000000..ddeeaa41a7585 --- /dev/null +++ b/test/elf/X86_64/note-sections-ro_plus_rw.test @@ -0,0 +1,42 @@ +# This tests the functionality that lld is able to recreate the note sections +# if they appear in the input, it looks like we need to differentiate RO note +# sections from RW note sections, and each creating a segment of its own + +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/note_ro_rw.o \ +RUN: --noinhibit-exec -o %t -static +RUN: llvm-readobj -sections %t | FileCheck -check-prefix=NOTESECTIONS %s +RUN: llvm-readobj -program-headers %t | FileCheck -check-prefix=NOTESEGMENT %s + +NOTESECTIONS: Section { +NOTESECTIONS: Name: .note.ro (1) +NOTESECTIONS: Type: SHT_NOTE (0x7) +NOTESECTIONS: Flags [ (0x2) +NOTESECTIONS: SHF_ALLOC (0x2) +NOTESECTIONS: ] +NOTESECTIONS: AddressAlignment: 4 +NOTESECTIONS: } +NOTESECTIONS: Section { +NOTESECTIONS: Name: .note.rw (31) +NOTESECTIONS: Type: SHT_NOTE (0x7) +NOTESECTIONS: Flags [ (0x3) +NOTESECTIONS: SHF_ALLOC (0x2) +NOTESECTIONS: SHF_WRITE (0x1) +NOTESECTIONS: ] +NOTESECTIONS: Size: 28 +NOTESECTIONS: AddressAlignment: 4 +NOTESECTIONS: } +NOTESEGMENT: ProgramHeader { +NOTESEGMENT: Type: PT_NOTE (0x4) +NOTESEGMENT: Flags [ (0x4) +NOTESEGMENT: PF_R (0x4) +NOTESEGMENT: ] +NOTESEGMENT: Alignment: 4 +NOTESEGMENT: } +NOTESEGMENT: ProgramHeader { +NOTESEGMENT: Type: PT_NOTE (0x4) +NOTESEGMENT: Flags [ (0x6) +NOTESEGMENT: PF_R (0x4) +NOTESEGMENT: PF_W (0x2) +NOTESEGMENT: ] +NOTESEGMENT: Alignment: 4 +NOTESEGMENT: } diff --git a/test/elf/X86_64/note-sections.test b/test/elf/X86_64/note-sections.test new file mode 100644 index 0000000000000..a49f95cf4a27f --- /dev/null +++ b/test/elf/X86_64/note-sections.test @@ -0,0 +1,23 @@ +# This tests the functionality that lld is able to recreate the note sections +# if they appear in the input + +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/note.o \ +RUN: --noinhibit-exec -o %t -static +RUN: llvm-readobj -sections %t | FileCheck -check-prefix=NOTESECTIONS %s +RUN: llvm-readobj -program-headers %t | FileCheck -check-prefix=NOTESEGMENT %s + + +NOTESECTIONS: Section { +NOTESECTIONS: Index: 1 +NOTESECTIONS: Name: .note.ident (1) +NOTESECTIONS: Type: SHT_NOTE (0x7) +NOTESECTIONS: Size: 28 +NOTESECTIONS: AddressAlignment: 4 +NOTESECTIONS: } + +NOTESEGMENT: ProgramHeader { +NOTESEGMENT: Type: PT_NOTE (0x4) +NOTESEGMENT: FileSize: 28 +NOTESEGMENT: MemSize: 28 +NOTESEGMENT: Alignment: 4 +NOTESEGMENT: } diff --git a/test/elf/X86_64/omagic.test b/test/elf/X86_64/omagic.test new file mode 100644 index 0000000000000..437d2e2a9f981 --- /dev/null +++ b/test/elf/X86_64/omagic.test @@ -0,0 +1,237 @@ +# This tests verifies functionality of omagic that we create only two segments, +# PT_LOAD, PT_TLS +# The data segment should not be aligned to a page boundary +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/nmagic.o \ +RUN: --noinhibit-exec -o %t --omagic -static +RUN: llvm-readobj -sections %t | FileCheck -check-prefix=OMAGICSECTIONS %s +RUN: llvm-readobj -program-headers %t | FileCheck -check-prefix=OMAGICPROGRAMHEADERS %s + +OMAGICSECTIONS: Section { +OMAGICSECTIONS: Name: (0) +OMAGICSECTIONS: Type: SHT_NULL (0x0) +OMAGICSECTIONS: Flags [ (0x0) +OMAGICSECTIONS: ] +OMAGICSECTIONS: Address: 0x0 +OMAGICSECTIONS: Offset: 0x0 +OMAGICSECTIONS: Size: 0 +OMAGICSECTIONS: Link: 0 +OMAGICSECTIONS: Info: 0 +OMAGICSECTIONS: AddressAlignment: 0 +OMAGICSECTIONS: EntrySize: 0 +OMAGICSECTIONS: } +OMAGICSECTIONS: Section { +OMAGICSECTIONS: Name: .text +OMAGICSECTIONS: Type: SHT_PROGBITS (0x1) +OMAGICSECTIONS: Flags [ (0x6) +OMAGICSECTIONS: SHF_ALLOC (0x2) +OMAGICSECTIONS: SHF_EXECINSTR (0x4) +OMAGICSECTIONS: ] +OMAGICSECTIONS: Address: 0x4000E8 +OMAGICSECTIONS: Offset: 0xE8 +OMAGICSECTIONS: Size: 11 +OMAGICSECTIONS: Link: 0 +OMAGICSECTIONS: Info: 0 +OMAGICSECTIONS: AddressAlignment: 4 +OMAGICSECTIONS: EntrySize: 0 +OMAGICSECTIONS: } +OMAGICSECTIONS: Section { +OMAGICSECTIONS: Name: .eh_frame +OMAGICSECTIONS: Type: SHT_PROGBITS (0x1) +OMAGICSECTIONS: Flags [ (0x2) +OMAGICSECTIONS: SHF_ALLOC (0x2) +OMAGICSECTIONS: ] +OMAGICSECTIONS: Address: 0x4000F8 +OMAGICSECTIONS: Offset: 0xF8 +OMAGICSECTIONS: Size: 56 +OMAGICSECTIONS: Link: 0 +OMAGICSECTIONS: Info: 0 +OMAGICSECTIONS: AddressAlignment: 8 +OMAGICSECTIONS: EntrySize: 0 +OMAGICSECTIONS: } +OMAGICSECTIONS: Section { +OMAGICSECTIONS: Name: .eh_frame_hdr +OMAGICSECTIONS: Type: SHT_PROGBITS (0x1) +OMAGICSECTIONS: Flags [ (0x2) +OMAGICSECTIONS: SHF_ALLOC (0x2) +OMAGICSECTIONS: ] +OMAGICSECTIONS: Address: 0x400130 +OMAGICSECTIONS: Offset: 0x130 +OMAGICSECTIONS: Size: 8 +OMAGICSECTIONS: Link: 0 +OMAGICSECTIONS: Info: 0 +OMAGICSECTIONS: AddressAlignment: 8 +OMAGICSECTIONS: EntrySize: 0 +OMAGICSECTIONS: } +OMAGICSECTIONS: Section { +OMAGICSECTIONS: Name: .tdata +OMAGICSECTIONS: Type: SHT_PROGBITS (0x1) +OMAGICSECTIONS: Flags [ (0x403) +OMAGICSECTIONS: SHF_ALLOC (0x2) +OMAGICSECTIONS: SHF_TLS (0x400) +OMAGICSECTIONS: SHF_WRITE (0x1) +OMAGICSECTIONS: ] +OMAGICSECTIONS: Address: 0x400138 +OMAGICSECTIONS: Offset: 0x138 +OMAGICSECTIONS: Size: 4 +OMAGICSECTIONS: Link: 0 +OMAGICSECTIONS: Info: 0 +OMAGICSECTIONS: AddressAlignment: 4 +OMAGICSECTIONS: EntrySize: 0 +OMAGICSECTIONS: } +OMAGICSECTIONS: Section { +OMAGICSECTIONS: Name: .tbss +OMAGICSECTIONS: Type: SHT_NOBITS (0x8) +OMAGICSECTIONS: Flags [ (0x403) +OMAGICSECTIONS: SHF_ALLOC (0x2) +OMAGICSECTIONS: SHF_TLS (0x400) +OMAGICSECTIONS: SHF_WRITE (0x1) +OMAGICSECTIONS: ] +OMAGICSECTIONS: Address: 0x40013C +OMAGICSECTIONS: Offset: 0x13C +OMAGICSECTIONS: Size: 8 +OMAGICSECTIONS: Link: 0 +OMAGICSECTIONS: Info: 0 +OMAGICSECTIONS: AddressAlignment: 4 +OMAGICSECTIONS: EntrySize: 0 +OMAGICSECTIONS: } +OMAGICSECTIONS: Section { +OMAGICSECTIONS: Name: .got.plt +OMAGICSECTIONS: Type: SHT_PROGBITS (0x1) +OMAGICSECTIONS: Flags [ (0x3) +OMAGICSECTIONS: SHF_ALLOC (0x2) +OMAGICSECTIONS: SHF_WRITE (0x1) +OMAGICSECTIONS: ] +OMAGICSECTIONS: Address: 0x400140 +OMAGICSECTIONS: Offset: 0x140 +OMAGICSECTIONS: Size: 0 +OMAGICSECTIONS: Link: 0 +OMAGICSECTIONS: Info: 0 +OMAGICSECTIONS: AddressAlignment: 8 +OMAGICSECTIONS: EntrySize: 0 +OMAGICSECTIONS: } +OMAGICSECTIONS: Section { +OMAGICSECTIONS: Name: .data +OMAGICSECTIONS: Type: SHT_PROGBITS (0x1) +OMAGICSECTIONS: Flags [ (0x3) +OMAGICSECTIONS: SHF_ALLOC (0x2) +OMAGICSECTIONS: SHF_WRITE (0x1) +OMAGICSECTIONS: ] +OMAGICSECTIONS: Address: 0x400140 +OMAGICSECTIONS: Offset: 0x140 +OMAGICSECTIONS: Size: 4 +OMAGICSECTIONS: Link: 0 +OMAGICSECTIONS: Info: 0 +OMAGICSECTIONS: AddressAlignment: 4 +OMAGICSECTIONS: EntrySize: 0 +OMAGICSECTIONS: } +OMAGICSECTIONS: Section { +OMAGICSECTIONS: Name: .bss +OMAGICSECTIONS: Type: SHT_NOBITS (0x8) +OMAGICSECTIONS: Flags [ (0x3) +OMAGICSECTIONS: SHF_ALLOC (0x2) +OMAGICSECTIONS: SHF_WRITE (0x1) +OMAGICSECTIONS: ] +OMAGICSECTIONS: Address: 0x400144 +OMAGICSECTIONS: Offset: 0x144 +OMAGICSECTIONS: Size: 0 +OMAGICSECTIONS: Link: 0 +OMAGICSECTIONS: Info: 0 +OMAGICSECTIONS: AddressAlignment: 4 +OMAGICSECTIONS: EntrySize: 0 +OMAGICSECTIONS: } +OMAGICSECTIONS: Section { +OMAGICSECTIONS: Name: .comment +OMAGICSECTIONS: Type: SHT_PROGBITS (0x1) +OMAGICSECTIONS: Flags [ (0x0) +OMAGICSECTIONS: ] +OMAGICSECTIONS: Address: 0x0 +OMAGICSECTIONS: Offset: 0x144 +OMAGICSECTIONS: Size: 43 +OMAGICSECTIONS: Link: 0 +OMAGICSECTIONS: Info: 0 +OMAGICSECTIONS: AddressAlignment: 1 +OMAGICSECTIONS: EntrySize: 0 +OMAGICSECTIONS: } +OMAGICSECTIONS: Section { +OMAGICSECTIONS: Name: .note.GNU-stack +OMAGICSECTIONS: Type: SHT_PROGBITS (0x1) +OMAGICSECTIONS: Flags [ (0x0) +OMAGICSECTIONS: ] +OMAGICSECTIONS: Address: 0x0 +OMAGICSECTIONS: Offset: 0x16F +OMAGICSECTIONS: Size: 0 +OMAGICSECTIONS: Link: 0 +OMAGICSECTIONS: Info: 0 +OMAGICSECTIONS: AddressAlignment: 1 +OMAGICSECTIONS: EntrySize: 0 +OMAGICSECTIONS: } +OMAGICSECTIONS: Section { +OMAGICSECTIONS: Name: .shstrtab +OMAGICSECTIONS: Type: SHT_STRTAB (0x3) +OMAGICSECTIONS: Flags [ (0x0) +OMAGICSECTIONS: ] +OMAGICSECTIONS: Address: 0x0 +OMAGICSECTIONS: Offset: 0x16F +OMAGICSECTIONS: Size: 115 +OMAGICSECTIONS: Link: 0 +OMAGICSECTIONS: Info: 0 +OMAGICSECTIONS: AddressAlignment: 1 +OMAGICSECTIONS: EntrySize: 0 +OMAGICSECTIONS: } +OMAGICSECTIONS: Section { +OMAGICSECTIONS: Name: .symtab +OMAGICSECTIONS: Type: SHT_SYMTAB (0x2) +OMAGICSECTIONS: Flags [ (0x0) +OMAGICSECTIONS: ] +OMAGICSECTIONS: Address: 0x0 +OMAGICSECTIONS: Offset: 0x1E8 +OMAGICSECTIONS: Size: 504 +OMAGICSECTIONS: Link: 13 +OMAGICSECTIONS: Info: 2 +OMAGICSECTIONS: AddressAlignment: 8 +OMAGICSECTIONS: EntrySize: 24 +OMAGICSECTIONS: } +OMAGICSECTIONS: Section { +OMAGICSECTIONS: Name: .strtab +OMAGICSECTIONS: Type: SHT_STRTAB (0x3) +OMAGICSECTIONS: Flags [ (0x0) +OMAGICSECTIONS: ] +OMAGICSECTIONS: Address: 0x0 +OMAGICSECTIONS: Offset: 0x3E0 +OMAGICSECTIONS: Size: 231 +OMAGICSECTIONS: Link: 0 +OMAGICSECTIONS: Info: 0 +OMAGICSECTIONS: AddressAlignment: 1 +OMAGICSECTIONS: EntrySize: 0 +OMAGICSECTIONS: } +OMAGICSECTIONS: ] + +OMAGICPROGRAMHEADERS: ProgramHeaders [ +OMAGICPROGRAMHEADERS: ProgramHeader { +OMAGICPROGRAMHEADERS: Type: PT_LOAD (0x1) +OMAGICPROGRAMHEADERS: Offset: 0x0 +OMAGICPROGRAMHEADERS: VirtualAddress: 0x400000 +OMAGICPROGRAMHEADERS: PhysicalAddress: 0x400000 +OMAGICPROGRAMHEADERS: FileSize: 324 +OMAGICPROGRAMHEADERS: MemSize: 324 +OMAGICPROGRAMHEADERS: Flags [ (0x7) +OMAGICPROGRAMHEADERS: PF_R (0x4) +OMAGICPROGRAMHEADERS: PF_W (0x2) +OMAGICPROGRAMHEADERS: PF_X (0x1) +OMAGICPROGRAMHEADERS: ] +OMAGICPROGRAMHEADERS: Alignment: 8 +OMAGICPROGRAMHEADERS: } +OMAGICPROGRAMHEADERS: ProgramHeader { +OMAGICPROGRAMHEADERS: Type: PT_TLS (0x7) +OMAGICPROGRAMHEADERS: Offset: 0x138 +OMAGICPROGRAMHEADERS: VirtualAddress: 0x400138 +OMAGICPROGRAMHEADERS: PhysicalAddress: 0x400138 +OMAGICPROGRAMHEADERS: FileSize: 4 +OMAGICPROGRAMHEADERS: MemSize: 12 +OMAGICPROGRAMHEADERS: Flags [ (0x6) +OMAGICPROGRAMHEADERS: PF_R (0x4) +OMAGICPROGRAMHEADERS: PF_W (0x2) +OMAGICPROGRAMHEADERS: ] +OMAGICPROGRAMHEADERS: Alignment: 4 +OMAGICPROGRAMHEADERS: } +OMAGICPROGRAMHEADERS: ] diff --git a/test/elf/X86_64/outputsegments.test b/test/elf/X86_64/outputsegments.test new file mode 100644 index 0000000000000..ab6ba8deb47da --- /dev/null +++ b/test/elf/X86_64/outputsegments.test @@ -0,0 +1,189 @@ +# Tests that lld does not create separate segment if the input sections are part +# of the same output section + +# Build executable +# RUN: yaml2obj -format=elf -docnum 1 %s -o %t.o +# RUN: lld -flavor gnu -target x86_64 %t.o -o %t1.exe -static \ +# RUN: --no-align-segments --noinhibit-exec +# RUN: lld -flavor gnu -target x86_64 %t.o -o %t2.exe -static \ +# RUN: --noinhibit-exec +# RUN: llvm-readobj -program-headers %t1.exe | FileCheck %s -check-prefix=SEGMENTS +# RUN: llvm-readobj -program-headers %t2.exe | FileCheck %s -check-prefix=SEGMENTS +# +#SEGMENTS: VirtualAddress: 0x400000 +#SEGMENTS: PhysicalAddress: 0x400000 +#SEGMENTS: FileSize: 288 +#SEGMENTS: MemSize: 288 +#SEGMENTS: VirtualAddress: 0x404000 +#SEGMENTS: PhysicalAddress: 0x404000 +#SEGMENTS: FileSize: 16608 +#SEGMENTS: MemSize: 16608 +#SEGMENTS: Alignment: 16384 +# +# object +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .text.foo + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000002000 + Content: 554889E54883EC1048BF0000000000000000B000E800000000B9000000008945FC89C84883C4105DC3 + - Name: .rela.text.foo + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text.foo + Relocations: + - Offset: 0x000000000000000A + Symbol: .rodata.str1.1 + Type: R_X86_64_64 + Addend: 0 + - Offset: 0x0000000000000015 + Symbol: printf + Type: R_X86_64_PC32 + Addend: -4 + - Name: .text.bar + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000004000 + Content: 554889E54883EC1048BF0000000000000000B000E800000000B9000000008945FC89C84883C4105DC3 + - Name: .rela.text.bar + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text.bar + Relocations: + - Offset: 0x000000000000000A + Symbol: .rodata.str1.1 + Type: R_X86_64_64 + Addend: 7 + - Offset: 0x0000000000000015 + Symbol: printf + Type: R_X86_64_PC32 + Addend: -4 + - Name: .text.main + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E54883EC10C745FC00000000E8000000008945F8E8000000008B4DF801C189C84883C4105DC3 + - Name: .rela.text.main + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text.main + Relocations: + - Offset: 0x0000000000000010 + Symbol: foo + Type: R_X86_64_PC32 + Addend: -4 + - Offset: 0x0000000000000018 + Symbol: bar + Type: R_X86_64_PC32 + Addend: -4 + - Name: .rodata.str1.1 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 48656C6C6F0A00576F726C640A00 + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 005562756E747520636C616E672076657273696F6E20332E352E302D73766E3231373330342D317E6578703120286272616E636865732F72656C656173655F33352920286261736564206F6E204C4C564D20332E352E302900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: 1400000000000000037A5200017810011B0C070890010000180000001C000000000000002900000000410E108602430D060000001800000038000000000000002900000000410E108602430D060000001C00000054000000000000002900000000410E108602430D0600000000000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .eh_frame + Relocations: + - Offset: 0x0000000000000020 + Symbol: .text.foo + Type: R_X86_64_PC32 + Addend: 0 + - Offset: 0x000000000000003C + Symbol: .text.bar + Type: R_X86_64_PC32 + Addend: 0 + - Offset: 0x0000000000000058 + Symbol: .text.main + Type: R_X86_64_PC32 + Addend: 0 +Symbols: + Local: + - Name: 1.c + Type: STT_FILE + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .text.foo + Type: STT_SECTION + Section: .text.foo + - Name: .text.bar + Type: STT_SECTION + Section: .text.bar + - Name: .text.main + Type: STT_SECTION + Section: .text.main + - Name: .rodata.str1.1 + Type: STT_SECTION + Section: .rodata.str1.1 + - Name: .comment + Type: STT_SECTION + Section: .comment + - Name: .note.GNU-stack + Type: STT_SECTION + Section: .note.GNU-stack + - Name: .eh_frame + Type: STT_SECTION + Section: .eh_frame + Global: + - Name: bar + Type: STT_FUNC + Section: .text.bar + Size: 0x0000000000000029 + - Name: foo + Type: STT_FUNC + Section: .text.foo + Size: 0x0000000000000029 + - Name: main + Type: STT_FUNC + Section: .text.main + Size: 0x0000000000000029 + - Name: printf +... diff --git a/test/elf/X86_64/reloc_r_x86_64_16.test b/test/elf/X86_64/reloc_r_x86_64_16.test new file mode 100644 index 0000000000000..7cca839eb623d --- /dev/null +++ b/test/elf/X86_64/reloc_r_x86_64_16.test @@ -0,0 +1,60 @@ +# Tests that lld can handle relocations of type R_X86_64_16 +#RUN: yaml2obj -format=elf -docnum 1 %s -o %t1.o +#RUN: lld -flavor gnu -target x86_64 %t1.o --noinhibit-exec -o %t2.out -static +#RUN: llvm-objdump -s %t2.out | FileCheck %s +#CHECK: Contents of section .data: +#CHECK: 401000 0210 +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: '0000' + - Name: .rela.data + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .data + Relocations: + - Offset: 0x0000000000000000 + Symbol: foo + Type: R_X86_64_16 + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + Global: + - Name: bar + Type: STT_OBJECT + Section: .data + Size: 0x0000000000000008 + - Name: foo + Type: STT_OBJECT + Section: .data + Value: 0x0000000000000002 + Size: 0x0000000000000002 +... diff --git a/test/elf/X86_64/reloc_r_x86_64_pc16.test b/test/elf/X86_64/reloc_r_x86_64_pc16.test new file mode 100644 index 0000000000000..6e43e5fade618 --- /dev/null +++ b/test/elf/X86_64/reloc_r_x86_64_pc16.test @@ -0,0 +1,61 @@ +# Tests that lld can handle relocations of type R_X86_64_PC16 +#RUN: yaml2obj -format=elf -docnum 1 %s -o %t1.o +#RUN: lld -flavor gnu -target x86_64 %t1.o --noinhibit-exec -o %t2.out -static +#RUN: llvm-objdump -s %t2.out | FileCheck %s +#CHECK: Contents of section .data: +#CHECK: 401000 0700 +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: '0000' + - Name: .rela.data + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .data + Relocations: + - Offset: 0x0000000000000000 + Symbol: foo + Type: R_X86_64_PC16 + Addend: 5 + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + Global: + - Name: bar + Type: STT_OBJECT + Section: .data + Size: 0x0000000000000008 + - Name: foo + Type: STT_OBJECT + Section: .data + Value: 0x0000000000000002 + Size: 0x0000000000000002 +... diff --git a/test/elf/X86_64/reloc_r_x86_64_pc64.test b/test/elf/X86_64/reloc_r_x86_64_pc64.test new file mode 100644 index 0000000000000..75744390be911 --- /dev/null +++ b/test/elf/X86_64/reloc_r_x86_64_pc64.test @@ -0,0 +1,61 @@ +# Tests that lld can handle relocations of type R_X86_64_PC64 +#RUN: yaml2obj -format=elf -docnum 1 %s -o %t1.o +#RUN: lld -flavor gnu -target x86_64 %t1.o --noinhibit-exec -o %t2.out -static +#RUN: llvm-objdump -s %t2.out | FileCheck %s +#CHECK: Contents of section .data: +#CHECK: 401000 0a00 +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: '0000' + - Name: .rela.data + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .data + Relocations: + - Offset: 0x0000000000000000 + Symbol: foo + Type: R_X86_64_PC64 + Addend: 8 + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + Global: + - Name: bar + Type: STT_OBJECT + Section: .data + Size: 0x0000000000000008 + - Name: foo + Type: STT_OBJECT + Section: .data + Value: 0x0000000000000002 + Size: 0x0000000000000002 +... diff --git a/test/elf/X86_64/rodata.test b/test/elf/X86_64/rodata.test new file mode 100644 index 0000000000000..61af99f65cfc6 --- /dev/null +++ b/test/elf/X86_64/rodata.test @@ -0,0 +1,9 @@ +# This tests that the ordinals for all merge atoms and defined atoms have been +# set properly + +RUN: lld -flavor gnu -target x86_64 %p/Inputs/rodata.o --noinhibit-exec \ +RUN: --merge-strings -static -o %t1 +RUN: llvm-nm -n %t1 | FileCheck %s + +CHECK: {{[0-9a-f]+}} R _nl_default_default_domain +CHECK: {{[0-9a-f]+}} R _nl_default_default_dirname diff --git a/test/elf/X86_64/sectionchoice.test b/test/elf/X86_64/sectionchoice.test new file mode 100644 index 0000000000000..4034d8b1111a7 --- /dev/null +++ b/test/elf/X86_64/sectionchoice.test @@ -0,0 +1,7 @@ +# This tests that we are able to properly set the sectionChoice for DefinedAtoms +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/zerosizedsection.o \ +RUN: --noinhibit-exec -o %t --output-filetype=yaml +RUN: FileCheck %s < %t + +CHECK-NOT: section-choice: sectionCustomRequired + diff --git a/test/elf/X86_64/sectionmap.test b/test/elf/X86_64/sectionmap.test new file mode 100644 index 0000000000000..a38f23e32b95c --- /dev/null +++ b/test/elf/X86_64/sectionmap.test @@ -0,0 +1,22 @@ +# This tests that we are able to merge the section .gcc_except_table, +# .data.rel.local, .data.rel.ro, any other sections that belong to .data +# into appropriate output sections +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/sectionmap.o \ +RUN: --noinhibit-exec -o %t +RUN: llvm-readobj -sections %t | FileCheck %s -check-prefix=VERIFYSECTIONHEADERS + +VERIFYSECTIONHEADERS: Section { +VERIFYSECTIONHEADERS: Name: .data +VERIFYSECTIONHEADERS: } +VERIFYSECTIONHEADERS: Section { +VERIFYSECTIONHEADERS: Name: .gcc_except_table +VERIFYSECTIONHEADERS: } +VERIFYSECTIONHEADERS: Section { +VERIFYSECTIONHEADERS: Name: .data.rel.local +VERIFYSECTIONHEADERS: } +VERIFYSECTIONHEADERS: Section { +VERIFYSECTIONHEADERS: Name: .data.rel.ro +VERIFYSECTIONHEADERS: } +VERIFYSECTIONHEADERS: Section { +VERIFYSECTIONHEADERS: Name: .bss +VERIFYSECTIONHEADERS: } diff --git a/test/elf/X86_64/startGroupEndGroup.test b/test/elf/X86_64/startGroupEndGroup.test new file mode 100644 index 0000000000000..ce1897683b34a --- /dev/null +++ b/test/elf/X86_64/startGroupEndGroup.test @@ -0,0 +1,48 @@ +# This tests functionality of --start-group, --end-group + +# This link should fail with unresolve symbol +RUN: not lld -flavor gnu -target x86_64 %p/Inputs/group/1.o \ +RUN: %p/Inputs/group/libfn.a %p/Inputs/group/libfn1.a -o x 2> %t.err + +# Test group +RUN: lld -flavor gnu -target x86_64 %p/Inputs/group/1.o --start-group \ +RUN: %p/Inputs/group/libfn.a %p/Inputs/group/libfn1.a --end-group -o %t1 + +# Mix object files in group +RUN: lld -flavor gnu -target x86_64 %p/Inputs/group/1.o --start-group \ +RUN: %p/Inputs/group/fn.o %p/Inputs/group/fn2.o \ +RUN: %p/Inputs/group/fn1.o --end-group -o %t2 + +# Mix Whole archive input, the group should not iterate the file libfn.a +RUN: lld -flavor gnu -target x86_64 %p/Inputs/group/1.o --start-group \ +RUN: --whole-archive %p/Inputs/group/libfn.a --no-whole-archive \ +RUN: %p/Inputs/group/libfn1.a --end-group -o %t3 + +# Defined symbols in a shared library. +RUN: lld -flavor gnu -target x86_64 %p/Inputs/group/1.o --start-group \ +RUN: %p/Inputs/group/libfn2.so %p/Inputs/group/fn1.o %p/Inputs/group/fn.o \ +RUN: --end-group -o %t4 + +# Test alias options too, as they are more widely used +# Test group +RUN: lld -flavor gnu -target x86_64 %p/Inputs/group/1.o '-(' \ +RUN: %p/Inputs/group/libfn.a %p/Inputs/group/libfn1.a '-)' -o %t1.alias + +# Mix object files in group +RUN: lld -flavor gnu -target x86_64 %p/Inputs/group/1.o '-(' \ +RUN: %p/Inputs/group/fn.o %p/Inputs/group/fn2.o \ +RUN: %p/Inputs/group/fn1.o '-)' -o %t2.alias + +# Mix Whole archive input, the group should not iterate the file libfn.a +RUN: lld -flavor gnu -target x86_64 %p/Inputs/group/1.o '-(' \ +RUN: --whole-archive %p/Inputs/group/libfn.a --no-whole-archive \ +RUN: %p/Inputs/group/libfn1.a '-)' -o %t3.alias + +RUN: llvm-nm %t1 | FileCheck -check-prefix=RESOLVEDEXTERNAL %s +RUN: llvm-nm %t2 | FileCheck -check-prefix=RESOLVEDEXTERNAL %s +RUN: llvm-nm %t3 | FileCheck -check-prefix=RESOLVEDEXTERNAL %s +RUN: llvm-nm %t1.alias | FileCheck -check-prefix=RESOLVEDEXTERNAL %s +RUN: llvm-nm %t2.alias | FileCheck -check-prefix=RESOLVEDEXTERNAL %s +RUN: llvm-nm %t3.alias | FileCheck -check-prefix=RESOLVEDEXTERNAL %s + +RESOLVEDEXTERNAL: {{[0-9a-z]+}} T fn2 diff --git a/test/elf/X86_64/startGroupEndGroupWithDynlib.test b/test/elf/X86_64/startGroupEndGroupWithDynlib.test new file mode 100644 index 0000000000000..3e40997db3848 --- /dev/null +++ b/test/elf/X86_64/startGroupEndGroupWithDynlib.test @@ -0,0 +1,10 @@ +# This tests functionality of --start-group, --end-group with a dynamic library + +# Mix dynamic libraries/object files in group +RUN: lld -flavor gnu -target x86_64 %p/Inputs/group/1.o --start-group \ +RUN: %p/Inputs/group/libfn.so %p/Inputs/group/fn2.o \ +RUN: %p/Inputs/group/fn1.o --end-group -o %t1 + +RUN: llvm-nm %t1 | FileCheck -check-prefix=RESOLVEDEXTERNAL %s + +RESOLVEDEXTERNAL: {{[0-9a-z]+}} T fn2 diff --git a/test/elf/X86_64/staticlib-search.test b/test/elf/X86_64/staticlib-search.test new file mode 100644 index 0000000000000..9c512571932d1 --- /dev/null +++ b/test/elf/X86_64/staticlib-search.test @@ -0,0 +1,6 @@ +# This tests the functionality for finding the static library libfn.a for ELF +RUN: lld -flavor gnu -target x86_64 %p/Inputs/main.o -L%p/Inputs/ -lfn -o %t \ +RUN: --noinhibit-exec -static -t 2> %t1 +RUN: FileCheck %s < %t1 + +CHECK: {{[\/0-9A-Za-z_]+}}libfn.a diff --git a/test/elf/X86_64/undef.test b/test/elf/X86_64/undef.test new file mode 100644 index 0000000000000..8f0039a146938 --- /dev/null +++ b/test/elf/X86_64/undef.test @@ -0,0 +1,18 @@ +# This tests the functionality that an undefined symbol thats defined in the +# commmand line pulls in the required object file from the archive library +# which is usually the usecase for it +RUN: lld -flavor gnu -target x86_64 -u fn %p/Inputs/libfn.a -o %t --noinhibit-exec +RUN: llvm-readobj -symbols %t | FileCheck -check-prefix=SYMFROMARCHIVE %s +RUN: lld -flavor gnu -target x86_64 %p/Inputs/libfn.a -o %t --noinhibit-exec +RUN: llvm-readobj -symbols %t | FileCheck %s + +SYMFROMARCHIVE: Symbol { +SYMFROMARCHIVE: Name: fn ({{[0-9]+}} +SYMFROMARCHIVE: Size: 11 +SYMFROMARCHIVE: Binding: Global (0x1) +SYMFROMARCHIVE: Type: Function (0x2) +SYMFROMARCHIVE: Other: 0 +SYMFROMARCHIVE: Section: .text +SYMFROMARCHIVE: } + +CHECK-NOT: Name: fn diff --git a/test/elf/X86_64/underscore-end.test b/test/elf/X86_64/underscore-end.test new file mode 100644 index 0000000000000..337aa197f812a --- /dev/null +++ b/test/elf/X86_64/underscore-end.test @@ -0,0 +1,81 @@ +# This tests verifies that the value of _end symbol is point to the right value +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/nmagic.o \ +RUN: --noinhibit-exec -o %t --nmagic +RUN: llvm-readobj -symbols %t | FileCheck -check-prefix=NMAGICABSSYMBOLS %s +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/nmagic.o \ +RUN: --noinhibit-exec -o %t --omagic +RUN: llvm-readobj -symbols %t | FileCheck -check-prefix=OMAGICABSSYMBOLS %s + +NMAGICABSSYMBOLS: Symbol { +NMAGICABSSYMBOLS: Name: __bss_start ({{[0-9]+}} +NMAGICABSSYMBOLS: Value: 0x40100C +NMAGICABSSYMBOLS: Size: 0 +NMAGICABSSYMBOLS: Binding: Global (0x1) +NMAGICABSSYMBOLS: Type: Object (0x1) +NMAGICABSSYMBOLS: Other: 0 +NMAGICABSSYMBOLS: Section: Absolute (0xFFF1) +NMAGICABSSYMBOLS: } +NMAGICABSSYMBOLS: Symbol { +NMAGICABSSYMBOLS: Name: __bss_end ({{[0-9]+}} +NMAGICABSSYMBOLS: Value: 0x40100C +NMAGICABSSYMBOLS: Size: 0 +NMAGICABSSYMBOLS: Binding: Global (0x1) +NMAGICABSSYMBOLS: Type: Object (0x1) +NMAGICABSSYMBOLS: Other: 0 +NMAGICABSSYMBOLS: Section: Absolute (0xFFF1) +NMAGICABSSYMBOLS: } +NMAGICABSSYMBOLS: Symbol { +NMAGICABSSYMBOLS: Name: _end ({{[0-9]+}} +NMAGICABSSYMBOLS: Value: 0x40100C +NMAGICABSSYMBOLS: Size: 0 +NMAGICABSSYMBOLS: Binding: Global (0x1) +NMAGICABSSYMBOLS: Type: Object (0x1) +NMAGICABSSYMBOLS: Other: 0 +NMAGICABSSYMBOLS: Section: Absolute (0xFFF1) +NMAGICABSSYMBOLS: } +NMAGICABSSYMBOLS: Symbol { +NMAGICABSSYMBOLS: Name: end ({{[0-9]+}} +NMAGICABSSYMBOLS: Value: 0x40100C +NMAGICABSSYMBOLS: Size: 0 +NMAGICABSSYMBOLS: Binding: Global (0x1) +NMAGICABSSYMBOLS: Type: Object (0x1) +NMAGICABSSYMBOLS: Other: 0 +NMAGICABSSYMBOLS: Section: Absolute (0xFFF1) +NMAGICABSSYMBOLS: } + +OMAGICABSSYMBOLS: Symbol { +OMAGICABSSYMBOLS: Name: __bss_start ({{[0-9]+}}) +OMAGICABSSYMBOLS: Value: 0x400144 +OMAGICABSSYMBOLS: Size: 0 +OMAGICABSSYMBOLS: Binding: Global (0x1) +OMAGICABSSYMBOLS: Type: Object (0x1) +OMAGICABSSYMBOLS: Other: 0 +OMAGICABSSYMBOLS: Section: Absolute (0xFFF1) +OMAGICABSSYMBOLS: } +OMAGICABSSYMBOLS: Symbol { +OMAGICABSSYMBOLS: Name: __bss_end ({{[0-9]+}} +OMAGICABSSYMBOLS: Value: 0x400144 +OMAGICABSSYMBOLS: Size: 0 +OMAGICABSSYMBOLS: Binding: Global (0x1) +OMAGICABSSYMBOLS: Type: Object (0x1) +OMAGICABSSYMBOLS: Other: 0 +OMAGICABSSYMBOLS: Section: Absolute (0xFFF1) +OMAGICABSSYMBOLS: } +OMAGICABSSYMBOLS: Symbol { +OMAGICABSSYMBOLS: Name: _end ({{[0-9]+}} +OMAGICABSSYMBOLS: Value: 0x400144 +OMAGICABSSYMBOLS: Size: 0 +OMAGICABSSYMBOLS: Binding: Global (0x1) +OMAGICABSSYMBOLS: Type: Object (0x1) +OMAGICABSSYMBOLS: Other: 0 +OMAGICABSSYMBOLS: Section: Absolute (0xFFF1) +OMAGICABSSYMBOLS: } +OMAGICABSSYMBOLS: Symbol { +OMAGICABSSYMBOLS: Name: end ({{[0-9]+}} +OMAGICABSSYMBOLS: Value: 0x400144 +OMAGICABSSYMBOLS: Size: 0 +OMAGICABSSYMBOLS: Binding: Global (0x1) +OMAGICABSSYMBOLS: Type: Object (0x1) +OMAGICABSSYMBOLS: Other: 0 +OMAGICABSSYMBOLS: Section: Absolute (0xFFF1) +OMAGICABSSYMBOLS: } diff --git a/test/elf/X86_64/weak-override.test b/test/elf/X86_64/weak-override.test new file mode 100644 index 0000000000000..b68b449a66494 --- /dev/null +++ b/test/elf/X86_64/weak-override.test @@ -0,0 +1,45 @@ +# Test for weak symbol getting overridden +RUN: lld -flavor gnu -target x86_64 %p/Inputs/weak.o %p/Inputs/ovrd.o \ +RUN: -o %t --noinhibit-exec +RUN: llvm-nm %t | FileCheck -check-prefix=WEAKORDER %s +RUN: lld -flavor gnu -target x86_64 %p/Inputs/weak.o \ +RUN: %p/Inputs/ovrd.o -o %t2 --output-filetype=yaml --noinhibit-exec +RUN: FileCheck -check-prefix=WEAKATOMSORDER %s < %t2 + +WEAKORDER: {{[0-9a-c]+}} T f + +WEAKATOMSORDER: references: +WEAKATOMSORDER: - kind: layout-after +WEAKATOMSORDER: offset: 0 +WEAKATOMSORDER: target: fn +WEAKATOMSORDER: - name: fn +WEAKATOMSORDER: references: +WEAKATOMSORDER: - kind: layout-after +WEAKATOMSORDER: offset: 0 +WEAKATOMSORDER: target: [[CONSTSTRA:[-a-zA-Z0-9_]+]] +WEAKATOMSORDER: - ref-name: [[CONSTSTRA]] +WEAKATOMSORDER: scope: global +WEAKATOMSORDER: content: [ 55, 48, 89, E5, BF, 00, 00, 00, 00, E8, 00, 00, +WEAKATOMSORDER: 00, 00, 5D, C3 ] +WEAKATOMSORDER: references: +WEAKATOMSORDER: - kind: layout-after +WEAKATOMSORDER: offset: 0 +WEAKATOMSORDER: target: main +WEAKATOMSORDER: - name: main +WEAKATOMSORDER: scope: global +WEAKATOMSORDER: content: [ 55, 48, 89, E5, B8, 00, 00, 00, 00, E8, 00, 00, +WEAKATOMSORDER: 00, 00, B8, 00, 00, 00, 00, 5D, C3 ] +WEAKATOMSORDER: references: +WEAKATOMSORDER: - kind: R_X86_64_PC32 +WEAKATOMSORDER: offset: 10 +WEAKATOMSORDER: target: f +WEAKATOMSORDER: addend: -4 +WEAKATOMSORDER: - ref-name: {{[0-9A-Z]+}} +WEAKATOMSORDER: references: +WEAKATOMSORDER: - kind: layout-after +WEAKATOMSORDER: offset: 0 +WEAKATOMSORDER: target: f +WEAKATOMSORDER: - name: f +WEAKATOMSORDER: scope: global +WEAKATOMSORDER: content: [ 55, 48, 89, E5, BF, 00, 00, 00, 00, E8, 00, 00, +WEAKATOMSORDER: 00, 00, 5D, C3 ] diff --git a/test/elf/X86_64/weak-zero-sized.test b/test/elf/X86_64/weak-zero-sized.test new file mode 100644 index 0000000000000..76e051064b9d9 --- /dev/null +++ b/test/elf/X86_64/weak-zero-sized.test @@ -0,0 +1,26 @@ +# Test for zero sized weak atoms, there is only a single weak atom +RUN: lld -flavor gnu -target x86_64 %p/Inputs/weak-zero-sized.o -o %t \ +RUN: --noinhibit-exec +RUN: llvm-nm %t | FileCheck -check-prefix=WEAKORDER %s +RUN: lld -flavor gnu -target x86_64 %p/Inputs/weak-zero-sized.o \ +RUN: --output-filetype=yaml -o %t2 --noinhibit-exec +RUN: FileCheck -check-prefix=WEAKATOMSORDER %s < %t2 + +WEAKORDER: 004001a4 T _start + +WEAKATOMSORDER: alignment: 2^2 +WEAKATOMSORDER: - kind: layout-after +WEAKATOMSORDER: offset: 0 +WEAKATOMSORDER: target: [[TARGETC:[-a-zA-Z0-9_]+]] +WEAKATOMSORDER: - name: [[TARGETA:[-a-zA-Z0-9_]+]] +WEAKATOMSORDER: scope: global +WEAKATOMSORDER: merge: as-weak +WEAKATOMSORDER: alignment: 2^2 +WEAKATOMSORDER: references: +WEAKATOMSORDER: - kind: layout-after +WEAKATOMSORDER: offset: 0 +WEAKATOMSORDER: target: [[TARGETC]] +WEAKATOMSORDER: - ref-name: [[TARGETC]] +WEAKATOMSORDER: scope: global +WEAKATOMSORDER: content: [ C3 ] +WEAKATOMSORDER: alignment: 2^2 diff --git a/test/elf/X86_64/weaksym.test b/test/elf/X86_64/weaksym.test new file mode 100644 index 0000000000000..d44ca8a369683 --- /dev/null +++ b/test/elf/X86_64/weaksym.test @@ -0,0 +1,78 @@ +# Tests that an executable with a weak undefine will put this symbol in the +# dynamic symbol table if the executable has a dynamic relocation against this +# symbol. + +#RUN: yaml2obj --format elf %s -o %t.o +#RUN: lld -flavor gnu -target x86_64 -e main %t.o -o %t1 +#RUN: llvm-readobj -dt %t1 | FileCheck -check-prefix CHECKSYMS %s + +#CHECKSYMS: Name: x@ +#CHECKSYMS-NEXT: Value: 0x0 +#CHECKSYMS-NEXT: Size: 0 +#CHECKSYMS-NEXT: Binding: Weak (0x2) +#CHECKSYMS-NEXT: Type: None (0x0) +#CHECKSYMS-NEXT: Other: 0 +#CHECKSYMS-NEXT: Section: Undefined (0x0) + +# The object file above corresponds to the following C program compiled with +# -fPIC: +# extern int *x __attribute__((weak)); +# +# int main() { +# if (x) +# return 1; +# return 0; +# } + +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E5488B0500000000C745FC00000000488138000000000F840C000000C745FC01000000E907000000C745FC000000008B45FC5DC3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x0000000000000007 + Symbol: x + Type: R_X86_64_GOTPCREL + Addend: -4 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + Global: + - Name: main + Type: STT_FUNC + Section: .text + Size: 0x0000000000000037 + - Name: _GLOBAL_OFFSET_TABLE_ + Weak: + - Name: x diff --git a/test/elf/X86_64/yamlinput.test b/test/elf/X86_64/yamlinput.test new file mode 100644 index 0000000000000..6e529b41b7a69 --- /dev/null +++ b/test/elf/X86_64/yamlinput.test @@ -0,0 +1,166 @@ +# This tests the functionality that lld is able to read +# an input YAML from a previous link + +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/initfini.o \ +RUN: --noinhibit-exec --output-filetype=yaml -o %t.objtxt +RUN: lld -flavor gnu -target x86_64-linux %t.objtxt \ +RUN: --noinhibit-exec -o %t1 +RUN: llvm-readobj -sections %t1 | FileCheck %s -check-prefix=SECTIONS + +SECTIONS: Section { +SECTIONS: Index: 0 +SECTIONS: Name: (0) +SECTIONS: Type: SHT_NULL (0x0) +SECTIONS: Flags [ (0x0) +SECTIONS: ] +SECTIONS: } +SECTIONS: Section { +SECTIONS: Index: 1 +SECTIONS: Name: .interp +SECTIONS: Type: SHT_PROGBITS (0x1) +SECTIONS: Flags [ (0x2) +SECTIONS: SHF_ALLOC (0x2) +SECTIONS: ] +SECTIONS: } +SECTIONS: Section { +SECTIONS: Index: 2 +SECTIONS: Name: .hash +SECTIONS: Type: SHT_HASH (0x5) +SECTIONS: Flags [ (0x2) +SECTIONS: SHF_ALLOC (0x2) +SECTIONS: ] +SECTIONS: } +SECTIONS: Section { +SECTIONS: Index: 3 +SECTIONS: Name: .dynsym +SECTIONS: Type: SHT_DYNSYM (0xB) +SECTIONS: Flags [ (0x2) +SECTIONS: SHF_ALLOC (0x2) +SECTIONS: ] +SECTIONS: } +SECTIONS: Section { +SECTIONS: Index: 4 +SECTIONS: Name: .dynstr +SECTIONS: Type: SHT_STRTAB (0x3) +SECTIONS: Flags [ (0x2) +SECTIONS: SHF_ALLOC (0x2) +SECTIONS: ] +SECTIONS: } +SECTIONS: Section { +SECTIONS: Index: 5 +SECTIONS: Name: .text +SECTIONS: Type: SHT_PROGBITS (0x1) +SECTIONS: Flags [ (0x6) +SECTIONS: SHF_ALLOC (0x2) +SECTIONS: SHF_EXECINSTR (0x4) +SECTIONS: ] +SECTIONS: } +SECTIONS: Section { +SECTIONS: Index: 6 +SECTIONS: Name: .rodata +SECTIONS: Type: SHT_PROGBITS (0x1) +SECTIONS: Flags [ (0x2) +SECTIONS: SHF_ALLOC (0x2) +SECTIONS: ] +SECTIONS: } +SECTIONS: Section { +SECTIONS: Index: 7 +SECTIONS: Name: .eh_frame +SECTIONS: Type: SHT_PROGBITS (0x1) +SECTIONS: Flags [ (0x2) +SECTIONS: SHF_ALLOC (0x2) +SECTIONS: ] +SECTIONS: } +SECTIONS: Section { +SECTIONS: Index: 8 +SECTIONS: Name: .eh_frame_hdr +SECTIONS: } +SECTIONS: Section { +SECTIONS: Index: 9 +SECTIONS: Name: .init_array +SECTIONS: Type: SHT_PROGBITS (0x1) +SECTIONS: Flags [ (0x3) +SECTIONS: SHF_ALLOC (0x2) +SECTIONS: SHF_WRITE (0x1) +SECTIONS: ] +SECTIONS: } +SECTIONS: Section { +SECTIONS: Index: 10 +SECTIONS: Name: .fini_array +SECTIONS: Type: SHT_PROGBITS (0x1) +SECTIONS: Flags [ (0x3) +SECTIONS: SHF_ALLOC (0x2) +SECTIONS: SHF_WRITE (0x1) +SECTIONS: ] +SECTIONS: } +SECTIONS: Section { +SECTIONS: Index: 11 +SECTIONS: Name: .dynamic +SECTIONS: Type: SHT_DYNAMIC (0x6) +SECTIONS: Flags [ (0x3) +SECTIONS: SHF_ALLOC (0x2) +SECTIONS: SHF_WRITE (0x1) +SECTIONS: ] +SECTIONS: } +SECTIONS: Section { +SECTIONS: Index: 12 +SECTIONS: Name: .got.plt +SECTIONS: Type: SHT_PROGBITS (0x1) +SECTIONS: Flags [ (0x3) +SECTIONS: SHF_ALLOC (0x2) +SECTIONS: SHF_WRITE (0x1) +SECTIONS: ] +SECTIONS: } +SECTIONS: Section { +SECTIONS: Index: 13 +SECTIONS: Name: .data +SECTIONS: Type: SHT_PROGBITS (0x1) +SECTIONS: Flags [ (0x3) +SECTIONS: SHF_ALLOC (0x2) +SECTIONS: SHF_WRITE (0x1) +SECTIONS: ] +SECTIONS: } +SECTIONS: Section { +SECTIONS: Index: 14 +SECTIONS: Name: .bss +SECTIONS: Type: SHT_NOBITS (0x8) +SECTIONS: Flags [ (0x3) +SECTIONS: SHF_ALLOC (0x2) +SECTIONS: SHF_WRITE (0x1) +SECTIONS: ] +SECTIONS: } +SECTIONS: Section { +SECTIONS: Index: 15 +SECTIONS: Name: .comment +SECTIONS: Type: SHT_PROGBITS (0x1) +SECTIONS: Flags [ (0x0) +SECTIONS: ] +SECTIONS: } +SECTIONS: Section { +SECTIONS: Index: 16 +SECTIONS: Name: .note.GNU-stack +SECTIONS: Type: SHT_PROGBITS (0x1) +SECTIONS: Flags [ (0x0) +SECTIONS: ] +SECTIONS: } +SECTIONS: Section { +SECTIONS: Index: 17 +SECTIONS: Name: .shstrtab +SECTIONS: Type: SHT_STRTAB (0x3) +SECTIONS: Flags [ (0x0) +SECTIONS: ] +SECTIONS: } +SECTIONS: Section { +SECTIONS: Index: 18 +SECTIONS: Name: .symtab +SECTIONS: Type: SHT_SYMTAB (0x2) +SECTIONS: Flags [ (0x0) +SECTIONS: ] +SECTIONS: } +SECTIONS: Section { +SECTIONS: Index: 19 +SECTIONS: Name: .strtab +SECTIONS: Type: SHT_STRTAB (0x3) +SECTIONS: Flags [ (0x0) +SECTIONS: ] +SECTIONS: } diff --git a/test/elf/abs-dup.objtxt b/test/elf/abs-dup.objtxt new file mode 100644 index 0000000000000..7340a29b0f00d --- /dev/null +++ b/test/elf/abs-dup.objtxt @@ -0,0 +1,19 @@ +# Tests handling an absolute symbol with no name +# RUN: lld -flavor gnu -target x86_64 -r %s \ +# RUN: --output-filetype=yaml | FileCheck %s + +absolute-atoms: + - name: abs + scope: static + value: 0x10 + - name: '' + scope: static + value: 0x15 + +# CHECK: absolute-atoms: +# CHECK: - name: abs +# CHECK: scope: static +# CHECK: value: 0x0000000000000010 +# CHECK: - name: '' +# CHECK: scope: static +# CHECK: value: 0x0000000000000015 diff --git a/test/elf/abs.test b/test/elf/abs.test new file mode 100644 index 0000000000000..bad74f10d194c --- /dev/null +++ b/test/elf/abs.test @@ -0,0 +1,19 @@ +# +# Source File: +# .local absGlobalSymbol +# .set absLocalSymbol,0xC0000 +# .type absLocalSymbol, @object +# .globl absGlobalSymbol +# .set absGlobalSymbol,0xD0000 +# .type absGlobalSymbol, @object + +# built using: "gcc -m32" +# +RUN: lld -flavor gnu -target i386 --output-filetype=yaml -r %p/Inputs/abs-test.i386 | FileCheck -check-prefix=YAML %s + +YAML: absolute-atoms: +YAML: - name: absLocalSymbol +YAML: value: {{0x[0]+C0000}} +YAML: - name: absGlobalSymbol +YAML: scope: global +YAML: value: {{0x[0]+D0000}} diff --git a/test/elf/allowduplicates.objtxt b/test/elf/allowduplicates.objtxt new file mode 100644 index 0000000000000..dbad3bd312ede --- /dev/null +++ b/test/elf/allowduplicates.objtxt @@ -0,0 +1,51 @@ +# RUN: lld -flavor gnu -target x86_64 --allow-multiple-definition %s \ +# RUN: --output-filetype=yaml --noinhibit-exec | FileCheck %s +# +# RUN: not lld -flavor gnu -target x86_64 %s --output-filetype=yaml \ +# RUN: --noinhibit-exec 2>&1 | FileCheck -check-prefix=ERROR %s +# +# RUN: lld -flavor gnu -target x86_64 -z muldefs %s \ +# RUN: --noinhibit-exec --output-filetype=yaml | FileCheck %s + +--- +defined-atoms: + - name: .text + alignment: 2^4 + section-choice: custom-required + section-name: .text + - name: main + scope: global + content: [ B8, 00, 00, 00, 00, C7, 44, 24, FC, 00, 00, 00, + 00, C3 ] + alignment: 2^4 + section-choice: custom-required + section-name: .text +--- +defined-atoms: + - name: .text + alignment: 2^4 + section-choice: custom-required + section-name: .text + - name: main + scope: global + content: [ B8, 00, 00, 00, 00, C7, 44, 24, FC, 00, 00, 00, + 00, C3 ] + alignment: 2^4 + section-choice: custom-required + section-name: .text +--- + +# CHECK: defined-atoms: +# CHECK: - name: .text +# CHECK: alignment: 2^4 +# CHECK: section-choice: custom-required +# CHECK: section-name: .text +# CHECK: - name: main +# CHECK: scope: global +# CHECK: content: [ B8, 00, 00, 00, 00, C7, 44, 24, FC, 00, 00, 00, +# CHECK: 00, C3 ] +# CHECK: alignment: 2^4 +# CHECK: section-choice: custom-required +# CHECK: section-name: .text + +# ERROR: duplicate symbol error diff --git a/test/elf/archive-elf-forceload.test b/test/elf/archive-elf-forceload.test new file mode 100644 index 0000000000000..a0d1150948155 --- /dev/null +++ b/test/elf/archive-elf-forceload.test @@ -0,0 +1,43 @@ +# Tests the functionality of archive libraries reading +# and resolution +# Note: The binary files would not be required once we have support to generate +# binary archives from textual(yaml) input +# +# Tests generated using the source files below +# main file +# int main() +# { +# fn(); +# return 0; +# } +# +# archive file +# int fn() +# { +# return 0; +# } +# +# int fn1() +# { +# return 0; +# } +# gcc -c main.c fn.c fn1.c + +RUN: lld -flavor gnu -target x86_64-linux -e main %p/Inputs/mainobj.x86_64 \ +RUN: --whole-archive %p/Inputs/libfnarchive.a --no-whole-archive --output-filetype=yaml \ +RUN: | FileCheck -check-prefix FORCELOAD %s + +FORCELOAD: defined-atoms: +FORCELOAD: - name: fn1 +FORCELOAD: scope: global +FORCELOAD: content: [ 55, 48, 89, E5, B8, 00, 00, 00, 00, 5D, C3 ] +FORCELOAD: - name: fn +FORCELOAD: scope: global +FORCELOAD: content: [ 55, 48, 89, E5, B8, 00, 00, 00, 00, 5D, C3 ] +FORCELOAD: absolute-atoms: +FORCELOAD: - name: main.c +FORCELOAD: value: 0x0 +FORCELOAD: - name: fn1.c +FORCELOAD: value: 0x0 +FORCELOAD: - name: fn.c +FORCELOAD: value: 0x0 diff --git a/test/elf/archive-elf.test b/test/elf/archive-elf.test new file mode 100644 index 0000000000000..ba6774644cbd8 --- /dev/null +++ b/test/elf/archive-elf.test @@ -0,0 +1,38 @@ +# Tests the functionality of archive libraries reading +# and resolution +# Note: The binary files would not be required once we have support to generate +# binary archives from textual(yaml) input +# +# Tests generated using the source files below +# main file +# int main() +# { +# fn(); +# return 0; +# } +# +# archive file +# int fn() +# { +# return 0; +# } +# +# int fn1() +# { +# return 0; +# } +# gcc -c main.c fn.c fn1.c + +RUN: lld -flavor gnu -target x86_64-linux --output-filetype=yaml -r \ +RUN: %p/Inputs/mainobj.x86_64 %p/Inputs/libfnarchive.a | \ +RUN: FileCheck -check-prefix NOFORCELOAD %s + +NOFORCELOAD: defined-atoms: +NOFORCELOAD: - name: fn +NOFORCELOAD: scope: global +NOFORCELOAD: content: [ 55, 48, 89, E5, B8, 00, 00, 00, 00, 5D, C3 ] +NOFORCELOAD: absolute-atoms: +NOFORCELOAD: - name: main.c +NOFORCELOAD: value: 0x0 +NOFORCELOAD: - name: fn.c +NOFORCELOAD: value: 0x0 diff --git a/test/elf/as-needed.test b/test/elf/as-needed.test new file mode 100644 index 0000000000000..4477f0fe0ca69 --- /dev/null +++ b/test/elf/as-needed.test @@ -0,0 +1,15 @@ +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/use-shared.x86-64 \ +RUN: --as-needed %p/Inputs/shared.so-x86-64 %p/Inputs/libifunc.x86-64.so \ +RUN: -o %t1 -e main --allow-shlib-undefined +RUN: llvm-readobj -dynamic-table %t1 | FileCheck %s -check-prefix AS_NEEDED + +AS_NEEDED: NEEDED SharedLibrary (shared.so-x86-64) +AS_NEEDED-NOT: NEEDED SharedLibrary (libifunc.x86-64.so) + +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/use-shared.x86-64 \ +RUN: %p/Inputs/shared.so-x86-64 %p/Inputs/libifunc.x86-64.so \ +RUN: -o %t2 -e main --allow-shlib-undefined +RUN: llvm-readobj -dynamic-table %t2 | FileCheck %s -check-prefix NO_AS_NEEDED + +NO_AS_NEEDED: NEEDED SharedLibrary (shared.so-x86-64) +NO_AS_NEEDED: NEEDED SharedLibrary (libifunc.x86-64.so) diff --git a/test/elf/branch.test b/test/elf/branch.test new file mode 100644 index 0000000000000..5e0b4a5aabf1e --- /dev/null +++ b/test/elf/branch.test @@ -0,0 +1,34 @@ +RUN: lld -flavor gnu -target hexagon -static --output-filetype=yaml \ +RUN: %p/Inputs/branch-test.hexagon %p/Inputs/target-test.hexagon --noinhibit-exec | FileCheck %s -check-prefix hexagon-yaml +RUN: lld -flavor gnu -target hexagon -e target -o %t1 \ +RUN: %p/Inputs/branch-test.hexagon %p/Inputs/target-test.hexagon --noinhibit-exec +RUN: llvm-readobj -h %t1 | FileCheck -check-prefix=hexagon-readobj %s + +hexagon-yaml: - name: back +hexagon-yaml: scope: global +hexagon-yaml: content: [ 00, C0, 00, 7F, 00, C0, 00, 5A, 00, 00, 00, 00, +hexagon-yaml: 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 ] +hexagon-yaml: references: +hexagon-yaml: - kind: +hexagon-yaml: offset: 4 +hexagon-yaml: target: target + +hexagon-yaml: - name: target +hexagon-yaml: scope: global +hexagon-yaml: content: [ 00, C0, 00, 5A ] +hexagon-yaml: references: +hexagon-yaml: - kind: +hexagon-yaml: offset: 0 +hexagon-yaml: target: back + + +hexagon-readobj: ElfHeader { +hexagon-readobj: Ident { +hexagon-readobj: Class: 32-bit (0x1) +hexagon-readobj: DataEncoding: LittleEndian (0x1) +hexagon-readobj: FileVersion: 1 +hexagon-readobj: OS/ABI: SystemV (0x0) +hexagon-readobj: ABIVersion: 0 +hexagon-readobj: } +hexagon-readobj: Type: Executable (0x2) +hexagon-readobj: Machine: EM_HEXAGON (0xA4) diff --git a/test/elf/check.test b/test/elf/check.test new file mode 100644 index 0000000000000..336b7fc1335c8 --- /dev/null +++ b/test/elf/check.test @@ -0,0 +1,39 @@ +# This tests the basic functionality of ordering data and functions as they +# appear in the inputs +RUN: lld -flavor gnu -target i386 -e global_func --noinhibit-exec --output-filetype=yaml \ +RUN: %p/Inputs/object-test.elf-i386 -o %t +RUN: FileCheck %s -check-prefix ELF-i386 < %t +RUN: lld -flavor gnu -target hexagon -e global_func --noinhibit-exec --output-filetype=yaml \ +RUN: %p/Inputs/object-test.elf-hexagon -o %t1 +RUN: FileCheck %s -check-prefix ELF-hexagon < %t1 + +ELF-i386: defined-atoms: +ELF-i386: - name: global_func +ELF-i386: - name: static_func +ELF-i386: - name: weak_func +ELF-i386: - name: hidden_func +ELF-i386: - name: no_dead_strip +ELF-i386: - name: no_special_section_func +ELF-i386: - name: global_variable +ELF-i386: - name: uninitialized_static_variable +ELF-i386: - name: special_section_func +ELF-i386: undefined-atoms: +ELF-i386: - name: puts +ELF-i386: absolute-atoms: +ELF-i386: - name: sample.c + +ELF-hexagon: - name: global_func +ELF-hexagon: - name: static_func +ELF-hexagon: - name: weak_func +ELF-hexagon: - name: hidden_func +ELF-hexagon: - name: no_dead_strip +ELF-hexagon: - name: no_special_section_func +ELF-hexagon: - name: global_variable +ELF-hexagon: - name: uninitialized_static_variable +ELF-hexagon: - name: special_section_func +ELF-hexagon: undefined-atoms: +ELF-hexagon: - name: puts +ELF-hexagon: absolute-atoms: +ELF-hexagon: - name: sample.c +ELF-hexagon: scope: static +ELF-hexagon: value: 0x0000000000000000 diff --git a/test/elf/checkrodata.test b/test/elf/checkrodata.test new file mode 100644 index 0000000000000..fc75657b4afb4 --- /dev/null +++ b/test/elf/checkrodata.test @@ -0,0 +1,9 @@ + +RUN: lld -flavor gnu -target i386 -o %t1 %p/Inputs/rodata-test.i386 --noinhibit-exec +RUN: llvm-objdump -section-headers %t1 | FileCheck -check-prefix=i386 %s +RUN: lld -flavor gnu -target hexagon -o %t2 %p/Inputs/rodata-test.hexagon --noinhibit-exec +RUN: llvm-objdump -section-headers %t2 | FileCheck -check-prefix=hexagon %s + +i386: .rodata 00000004 0000000000000114 DATA + +hexagon: .rodata 00000004 0000000000000114 DATA diff --git a/test/elf/common.test b/test/elf/common.test new file mode 100644 index 0000000000000..46fcfe39d4860 --- /dev/null +++ b/test/elf/common.test @@ -0,0 +1,10 @@ +RUN: lld -flavor gnu -target x86_64-linux -o %t %p/Inputs/relocs.x86-64 \ +RUN: -e _start -static +RUN: llvm-readobj -t %t | FileCheck %s + +CHECK: Symbol { +CHECK: Name: i +CHECK-NEXT: Value: +CHECK-NEXT: Size: +CHECK-NEXT: Binding: +CHECK-NEXT: Type: Object diff --git a/test/elf/consecutive-weak-sym-defs.test b/test/elf/consecutive-weak-sym-defs.test new file mode 100644 index 0000000000000..095fabb17ab86 --- /dev/null +++ b/test/elf/consecutive-weak-sym-defs.test @@ -0,0 +1,81 @@ +#Tests that multiple consecutive weak symbol definitions do not confuse the +#ELF reader. For example: +# +# my_weak_func1: +# my_weak_func2: +# my_weak_func3: +# code +# +#If my_weak_func2 is merged to other definition, this should not disturb the +#definition my_weak_func1 to "code". +# +# +#RUN: yaml2obj -format=elf %p/Inputs/consecutive-weak-defs.o.yaml -o=%t1.o +#RUN: yaml2obj -format=elf %p/Inputs/main-with-global-def.o.yaml -o=%t2.o +#RUN: lld -flavor gnu -target x86_64 %t1.o %t2.o -e=main -o %t1 +#RUN: obj2yaml %t1 | FileCheck -check-prefix CHECKLAYOUT %s +# +# Check that the layout has not been changed: +# +#CHECKLAYOUT: Name: .text +#CHECKLAYOUT-NEXT: Type: +#CHECKLAYOUT-NEXT: Flags: +#CHECKLAYOUT-NEXT: Address: +#CHECKLAYOUT-NEXT: AddressAlign: +#CHECKLAYOUT-NEXT: Content: 554889E5E8020000005DC3554889E5B8640000005DC3 +# ^~~> my_func ^~~> my_weak_func +# +# +# +#Our two input files were produced by the following code: +# +#Inputs/consecutive-weak-defs.o.yaml (this one is in assembly to allow us to +# easily define multiple labels) +# +# .text +# .globl my_func +# .type my_func,@function +# my_func: +# pushq %rbp +# movq %rsp, %rbp +# callq my_weak_func +# popq %rbp +# retq +# .Ltmp0: +# .size my_func, .Ltmp0-my_func +# +# .text +# .weak my_weak_func +# .type my_weak_func,@function +# .weak my_weak_func2 +# .type my_weak_func2,@function +# .weak my_weak_func3 +# .type my_weak_func3,@function +# my_weak_func: +# my_weak_func2: +# my_weak_func3: +# pushq %rbp +# movq %rsp, %rbp +# movl $100, %eax +# popq %rbp +# retq +# .Ltmp1: +# .size my_weak_func, .Ltmp1-my_weak_func +# .size my_weak_func2, .Ltmp1-my_weak_func2 +# .size my_weak_func3, .Ltmp1-my_weak_func3 +# +#Inputs/main-with-global-def.o.yaml: +# +# int my_func(); +# +# int my_weak_func2() { +# return 200; +# } +# +# int main() { +# return my_func(); +# } +# +#------------------------------------------------------------------------------- +# The net effect is that this program should return 100. + diff --git a/test/elf/defsym.objtxt b/test/elf/defsym.objtxt new file mode 100644 index 0000000000000..e9c3922d59949 --- /dev/null +++ b/test/elf/defsym.objtxt @@ -0,0 +1,28 @@ +# RUN: lld -flavor gnu -target x86_64 --defsym=foo=0x1234 -r %s \ +# RUN: --output-filetype=yaml | FileCheck -check-prefix=ABS %s + +# RUN: lld -flavor gnu -target x86_64 --defsym=foo=main -r %s \ +# RUN: --output-filetype=yaml | FileCheck -check-prefix=ALIAS %s + +defined-atoms: + - name: main + scope: global + content: [ B8, 00, 00, 00, 00, C7, 44, 24, FC, 00, 00, 00, 00, C3 ] + alignment: 2^4 + section-choice: custom-required + section-name: .text + +# ABS: absolute-atoms: +# ABS: - name: foo +# ABS: scope: global +# ABS: value: 0x0000000000001234 + +# ALIAS: defined-atoms: +# ALIAS: - name: foo +# ALIAS: scope: global +# ALIAS: section-choice: custom-required +# ALIAS: section-name: .text +# ALIAS: references: +# ALIAS: - kind: layout-after +# ALIAS: offset: 0 +# ALIAS: target: main diff --git a/test/elf/dynamic-segorder.test b/test/elf/dynamic-segorder.test new file mode 100644 index 0000000000000..1fdccec9921b7 --- /dev/null +++ b/test/elf/dynamic-segorder.test @@ -0,0 +1,17 @@ +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/tls.x86-64 \ +RUN: %p/Inputs/shared.so-x86-64 -o %t -e main --allow-shlib-undefined \ +RUN: --defsym=__tls_get_addr=0 +RUN: llvm-objdump -p %t | FileCheck %s + +CHECK: PHDR +CHECK: flags r-x +CHECK: INTERP +CHECK: flags r-- +CHECK: LOAD +CHECK: flags r-x +CHECK: LOAD +CHECK: flags rw- +CHECK: DYNAMIC +CHECK: flags rw- +CHECK: TLS +CHECK: flags rw- diff --git a/test/elf/dynamic-undef.test b/test/elf/dynamic-undef.test new file mode 100644 index 0000000000000..7506b21b3ca70 --- /dev/null +++ b/test/elf/dynamic-undef.test @@ -0,0 +1,34 @@ +# +# This test creates a executable and tests the options that are used to +# to create an executable and a shared library +# +# This test will fail because there are unresolved symbols from the shared +# library and we are not passing --allow-shlib-undefined +RUN: not lld -flavor gnu -target x86_64-linux %p/Inputs/tls.x86-64 \ +RUN: %p/Inputs/shared.so-x86-64 -o %t -e main 2> %t1 +RUN: FileCheck -check-prefix=EXEC %s < %t1 +# This test will pass because of --allow-shlib-undefined +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/tls.x86-64 \ +RUN: %p/Inputs/shared.so-x86-64 -o %t -e main --allow-shlib-undefined \ +RUN: --defsym=__tls_get_addr=0 +# Building shared libraries should not fail when there is a undefined symbol. +# Test creation of shared library, this should pass because we are using +# shared option and by default, dynamic library wouldn't create undefined atoms +# from the input shared library +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/tls.x86-64 \ +RUN: %p/Inputs/shared.so-x86-64 -o %t.usenoundefines -e main -shared +RUN: llvm-readobj -symbols %t.usenoundefines | FileCheck %s -check-prefix=SHLIB-NOUNDEF +# Test creation of shared library, this should fail because we are using +# shared option setting the options to use the shared library undefines to +# create undefined atoms from the input shared library +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/tls.x86-64 \ +RUN: %p/Inputs/shared.so-x86-64 -o %t.useundefines -e main -shared \ +RUN: --use-shlib-undefines --no-allow-shlib-undefined 2> %t2 +RUN: llvm-readobj -symbols %t.useundefines | FileCheck -check-prefix=SHLIB-UNDEF-SYMBOLS %s + +EXEC: Undefined symbol: {{.+[\\/]}}shared.so-x86-64: puts +SHLIB: Undefined symbol: {{.+[\\/]}}shared.so-x86-64: puts +EXEC-NOT: Undefined symbol: {{.+[\\/]}}shared.so-x86-64: weakfoo +SHLIB-NOT: Undefined symbol: {{.+[\\/]}}shared.so-x86-64: weakfoo +SHLIB-NOUNDEF-NOT: Name: puts +SHLIB-UNDEF-SYMBOLS: Name: puts diff --git a/test/elf/dynamic.test b/test/elf/dynamic.test new file mode 100644 index 0000000000000..59269612cf8ef --- /dev/null +++ b/test/elf/dynamic.test @@ -0,0 +1,80 @@ +# Checks functionality of dynamic executables +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/use-shared.x86-64 \ +RUN: %p/Inputs/shared.so-x86-64 -o %t -e main --allow-shlib-undefined \ +RUN: -rpath /l1:/l2 -rpath /l3 +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/use-shared.x86-64 \ +RUN: %p/Inputs/shared.so-x86-64 --output-filetype=yaml -o %t2 --allow-shlib-undefined \ +RUN: --noinhibit-exec +RUN: llvm-objdump -p %t >> %t2 +RUN: llvm-readobj -s -dyn-symbols -dynamic-table %t >> %t2 +RUN: FileCheck %s < %t2 + +CHECK: name: main +CHECK: kind: R_X86_64_PC32 +CHECK: offset: 18 +CHECK: target: [[PLTNAME:[-a-zA-Z0-9_]+]] + +CHECK: name: [[PLTNAME]] +CHECK: type: stub + +CHECK: type: got +CHECK: references: +CHECK: kind: R_X86_64_JUMP_SLOT + +CHECK: shared-library-atoms: +CHECK: name: foo +CHECK: load-name: shared.so-x86-64 + +CHECK: PHDR off 0x{{0+}}40 +CHECK: INTERP +CHECK: flags r-- + +CHECK: Section { +CHECK: Name: .hash +CHECK-NEXT: Type: SHT_HASH +CHECK-NEXT: Flags [ +CHECK-NEXT: SHF_ALLOC +CHECK-NEXT: ] +CHECK-NEXT: Address: +CHECK-NEXT: Offset: +CHECK-NEXT: Size: 32 +CHECK-NEXT: Link: +CHECK-NEXT: Info: +CHECK-NEXT: AddressAlignment: 8 +CHECK-NEXT: EntrySize: +CHECK-NEXT: } + +CHECK: DynamicSymbols [ +CHECK: Symbol { +CHECK: Name: foo +CHECK-NEXT: Value: 0 +CHECK-NEXT: Size: +CHECK-NEXT: Binding: Global +CHECK-NEXT: Type: Function +CHECK: } +CHECK: Symbol { +CHECK: Name: i +CHECK-NEXT: Value: 0 +CHECK-NEXT: Size: +CHECK-NEXT: Binding: Global +CHECK-NEXT: Type: Object +CHECK: } + +CHECK: DynamicSection [ (15 entries) +CHECK: Tag Type Name/Value +CHECK: 0x0000000000000004 HASH +CHECK: 0x0000000000000005 STRTAB +CHECK: 0x0000000000000006 SYMTAB +CHECK: 0x000000000000000A STRSZ +CHECK: 0x000000000000000B SYMENT 24 +CHECK: 0x0000000000000007 RELA +CHECK: 0x0000000000000008 RELASZ 24 +CHECK: 0x0000000000000009 RELAENT 24 +CHECK: 0x0000000000000002 PLTRELSZ 24 +CHECK: 0x0000000000000003 PLTGOT +CHECK: 0x0000000000000014 PLTREL RELA +CHECK: 0x0000000000000017 JMPREL +CHECK: 0x0000000000000001 NEEDED SharedLibrary (shared.so-x86-64) +CHECK: 0x000000000000000F RPATH /l1:/l2:/l3 +CHECK: 0x0000000000000000 NULL 0x0 +CHECK: ] diff --git a/test/elf/eh_frame_hdr.test b/test/elf/eh_frame_hdr.test new file mode 100644 index 0000000000000..31429857ec698 --- /dev/null +++ b/test/elf/eh_frame_hdr.test @@ -0,0 +1,30 @@ +#RUN: yaml2obj -format=elf %s > %t +#RUN: lld -flavor gnu -target x86_64-linux %t --noinhibit-exec \ +#RUN: -o %t1 +#RUN: llvm-objdump -s %t1 | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 + +Sections: +- Name: .eh_frame + Type: SHT_PROGBITS + Content: "00" + AddressAlign: 8 + Flags: [SHF_ALLOC] + +Symbols: + Local: + - Name: .eh_frame + Type: STT_SECTION + Section: .eh_frame + +# CHECK: Contents of section .eh_frame: +# CHECK-NEXT: 4001e0 00 +# CHECK-NEXT: Contents of section .eh_frame_hdr: +# CHECK-NEXT: 4001e8 011bffff f4ffffff +# ^ 0x4001e0 - 0x4001e8 - 4 = 0xfffffff4 diff --git a/test/elf/entry.objtxt b/test/elf/entry.objtxt new file mode 100644 index 0000000000000..7e0c1623565a7 --- /dev/null +++ b/test/elf/entry.objtxt @@ -0,0 +1,58 @@ +# Tests entry point handling +# +# Test generated using the source file below: +# +# int main() +# { +# return 0; +# } +# + +# RUN: lld -flavor gnu -target x86_64 %s -e _entrypoint --noinhibit-exec -o %t1 +# RUN: llvm-nm -n %t1 | FileCheck %s +# +# CHECK: 004001e0 T main +# CHECK: 00401000 D _DYNAMIC +# CHECK: 00401060 A _end +# CHECK: 00401060 A end +# CHECK: U _entrypoint + +defined-atoms: + - name: .text + alignment: 2^4 + section-choice: custom-required + section-name: .text + - name: main + scope: global + content: [ B8, 00, 00, 00, 00, C7, 44, 24, FC, 00, 00, 00, + 00, C3 ] + alignment: 2^4 + section-choice: custom-required + section-name: .text + - name: .data + type: data + alignment: 2^2 + section-choice: custom-required + section-name: .data + - name: .bss + type: zero-fill + alignment: 2^2 + section-choice: custom-required + section-name: .bss + - name: .note.GNU-stack + section-choice: custom-required + section-name: .note.GNU-stack + permissions: r-- + - name: .eh_frame + content: [ 14, 00, 00, 00, 00, 00, 00, 00, 01, 7A, 52, 00, + 01, 78, 10, 01, 1B, 0C, 07, 08, 90, 01, 00, 00, + 14, 00, 00, 00, 1C, 00, 00, 00, 00, 00, 00, 00, + 0E, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 ] + alignment: 2^3 + section-choice: custom-required + section-name: .eh_frame + permissions: r-- + references: + - kind: R_X86_64_PC32 + offset: 32 + target: .text diff --git a/test/elf/export-dynamic.test b/test/elf/export-dynamic.test new file mode 100644 index 0000000000000..37876a47c8406 --- /dev/null +++ b/test/elf/export-dynamic.test @@ -0,0 +1,99 @@ +# Tests the --export-dynamic (-E) flag. When creating a dynamic executable and +# receiving this flag, the linker should export all globally visible symbols in +# its dynamic symbol table. + +#RUN: yaml2obj -format=elf %s -o=%t.o +#RUN: lld -flavor gnu -target x86_64 -E %t.o -e=main -o %t1 +#RUN: llvm-readobj -dt %t1 | FileCheck -check-prefix CHECKSYMS %s + +#CHECKSYMS: myfunc1@ +#CHECKSYMS: main@ +#CHECKSYMS: myvar1@ + +# The object file below was generated with the following code: +# +# (command line clang -c prog.c -o prog.o) +# +# int myvar1 = 22; +# +# static int mysecretvar = 11; +# +# int myfunc1() { +# return 23; +# } +# +# static int mysecretfunc() { +# return 42; +# } +# +# int main() { +# return mysecretfunc() + mysecretvar; +# } +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E5B8170000005DC30F1F440000554889E54883EC10C745FC00000000E81C000000030425000000004883C4105DC36666666666662E0F1F840000000000554889E5B82A0000005DC3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x0000000000000027 + Symbol: .data + Type: R_X86_64_32S + Addend: 4 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: 160000000B000000 + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' +Symbols: + Local: + - Name: mysecretfunc + Type: STT_FUNC + Section: .text + Value: 0x0000000000000040 + Size: 0x000000000000000B + - Name: mysecretvar + Type: STT_OBJECT + Section: .data + Value: 0x0000000000000004 + Size: 0x0000000000000004 + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + Global: + - Name: main + Type: STT_FUNC + Section: .text + Value: 0x0000000000000010 + Size: 0x0000000000000021 + - Name: myfunc1 + Type: STT_FUNC + Section: .text + Size: 0x000000000000000B + - Name: myvar1 + Type: STT_OBJECT + Section: .data + Size: 0x0000000000000004 diff --git a/test/elf/filenotfound.test b/test/elf/filenotfound.test new file mode 100644 index 0000000000000..d64568f2ea53c --- /dev/null +++ b/test/elf/filenotfound.test @@ -0,0 +1,3 @@ +# Check that a file that cannot be found results in a proper error message +RUN: not lld -flavor gnu -target x86_64 %p/Inputs/nofile.o 2>&1 | FileCheck %s +#CHECK: lld: cannot find file {{.+[\\/]}}nofile.o diff --git a/test/elf/gnulinkonce/gnulinkonce-report-discarded-reference.test b/test/elf/gnulinkonce/gnulinkonce-report-discarded-reference.test new file mode 100644 index 0000000000000..e1d0f8e7b55e5 --- /dev/null +++ b/test/elf/gnulinkonce/gnulinkonce-report-discarded-reference.test @@ -0,0 +1,147 @@ +# Tests that the linker is able to read .gnu.linkonce sections and link them +# appropriately. The testcase has been created by using the following source +# code. +# TODO: This test should produce a discarded reference error message which it +# does not currently. +# linkoncea.s +# .section .gnu.linkonce.d.dummy,"aw" +#bar: +# .long 0 +# linkonceb.s +# .section .gnu.linkonce.d.dummy,"aw" +#foo: +# .long 0 +# .section .blah, "aw" +# .long foo +#RUN: yaml2obj -format=elf -docnum 1 %s -o %t.linkonce1a.o +#RUN: yaml2obj -format=elf -docnum 2 %s -o %t.linkonce1b.o +#RUN: lld -flavor gnu -target x86_64 %t.linkonce1a.o %t.linkonce1b.o \ +#RUN: --noinhibit-exec --output-filetype=yaml -o %t2.out.yaml +#RUN: lld -flavor gnu -target x86_64 %t.linkonce1a.o %t.linkonce1b.o \ +#RUN: --noinhibit-exec -o %t2.out +#RUN: FileCheck %s -check-prefix=CHECKGNULINKONCE < %t2.out.yaml +#RUN: llvm-readobj -sections %t2.out | FileCheck %s -check-prefix=CHECKGNULINKONCESECTIONS +#CHECKGNULINKONCE: - name: .gnu.linkonce.d.dummy +#CHECKGNULINKONCE: scope: global +#CHECKGNULINKONCE: type: gnu-linkonce +#CHECKGNULINKONCE: section-choice: custom-required +#CHECKGNULINKONCE: section-name: .gnu.linkonce.d.dummy +#CHECKGNULINKONCE: permissions: rw- +#CHECKGNULINKONCE: references: +#CHECKGNULINKONCE: - kind: group-child +#CHECKGNULINKONCE: offset: 0 +#CHECKGNULINKONCE: target: bar +#CHECKGNULINKONCESECTIONS: Section { +#CHECKGNULINKONCESECTIONS: Name: .gnu.linkonce.d.dummy +#CHECKGNULINKONCESECTIONS: Type: SHT_PROGBITS +#CHECKGNULINKONCESECTIONS: Flags [ (0x3) +#CHECKGNULINKONCESECTIONS: SHF_ALLOC (0x2) +#CHECKGNULINKONCESECTIONS: SHF_WRITE (0x1) +#CHECKGNULINKONCESECTIONS: ] +#CHECKGNULINKONCESECTIONS: Size: 4 +#CHECKGNULINKONCESECTIONS: } +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .gnu.linkonce.d.dummy + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '00000000' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .gnu.linkonce.d.dummy + Type: STT_SECTION + Section: .gnu.linkonce.d.dummy + - Name: bar + Section: .gnu.linkonce.d.dummy +... +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .gnu.linkonce.d.dummy + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '00000000' + - Name: .blah + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '00000000' + - Name: .rela.blah + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .blah + Relocations: + - Offset: 0x0000000000000000 + Symbol: foo + Type: R_X86_64_32 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .gnu.linkonce.d.dummy + Type: STT_SECTION + Section: .gnu.linkonce.d.dummy + - Name: foo + Section: .gnu.linkonce.d.dummy + - Name: .blah + Type: STT_SECTION + Section: .blah +... diff --git a/test/elf/gnulinkonce/gnulinkonce-report-undef.test b/test/elf/gnulinkonce/gnulinkonce-report-undef.test new file mode 100644 index 0000000000000..c6d050dcd63ae --- /dev/null +++ b/test/elf/gnulinkonce/gnulinkonce-report-undef.test @@ -0,0 +1,129 @@ +# Tests that the linker is able to read .gnu.linkonce sections and link them +# appropriately. The testcase has been created by using the following source +# code. This test checks that the linker produces an undefined error. +# linkoncea.s +# .section .gnu.linkonce.d.dummy,"aw" +#bar: +# .long 0 +# linkonceb.s +# .section .gnu.linkonce.d.dummy,"aw" +# .global foo +#foo: +# .long 0 +# .section .blah, "aw" +# .long foo +#RUN: yaml2obj -format=elf -docnum 1 %s -o %t.linkonce1a.o +#RUN: yaml2obj -format=elf -docnum 2 %s -o %t.linkonce1b.o +#RUN: not lld -flavor gnu -target x86_64 %t.linkonce1a.o %t.linkonce1b.o \ +#RUN: --output-filetype=yaml -o %t2.out.yaml 2>&1 | FileCheck \ +#RUN: -check-prefix=UNDEFS %s +#RUN: not lld -flavor gnu -target x86_64 %t.linkonce1a.o %t.linkonce1b.o \ +#RUN: -o %t2.out 2>&1 | FileCheck -check-prefix=UNDEFS %s +#UNDEFS: Undefined symbol: {{.*}} foo +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .gnu.linkonce.d.dummy + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '00000000' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .gnu.linkonce.d.dummy + Type: STT_SECTION + Section: .gnu.linkonce.d.dummy + - Name: bar + Section: .gnu.linkonce.d.dummy +... +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .gnu.linkonce.d.dummy + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '00000000' + - Name: .blah + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '00000000' + - Name: .rela.blah + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .blah + Relocations: + - Offset: 0x0000000000000000 + Symbol: foo + Type: R_X86_64_32 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .gnu.linkonce.d.dummy + Type: STT_SECTION + Section: .gnu.linkonce.d.dummy + - Name: .blah + Type: STT_SECTION + Section: .blah + Global: + - Name: foo + Section: .gnu.linkonce.d.dummy +... diff --git a/test/elf/gnulinkonce/gnulinkonce.test b/test/elf/gnulinkonce/gnulinkonce.test new file mode 100644 index 0000000000000..17559f6563285 --- /dev/null +++ b/test/elf/gnulinkonce/gnulinkonce.test @@ -0,0 +1,151 @@ +# Tests that the linker is able to read .gnu.linkonce sections and link them +# appropriately. The testcase has been created by using the following source +# code +# linkonce1a.s +# ------------ +# .section .gnu.linkonce.d.dummy,"aw" +#bar: +# .long 0 +# linkonce1b.s +# ------------ +# .globl main +# .globl start +# .globl _start +# .globl __start +# .text +#main: +#start: +#_start: +#__start: +# .long 0 +# +# .section .gnu.linkonce.d.dummy,"aw" +#foo: +# .long 0 +#RUN: yaml2obj -format=elf -docnum 1 %s -o %t.linkonce1a.o +#RUN: yaml2obj -format=elf -docnum 2 %s -o %t.linkonce1b.o +#RUN: lld -flavor gnu -target x86_64 %t.linkonce1a.o %t.linkonce1b.o \ +#RUN: --noinhibit-exec --output-filetype=yaml -o %t2.out.yaml +#RUN: lld -flavor gnu -target x86_64 %t.linkonce1a.o %t.linkonce1b.o \ +#RUN: --noinhibit-exec -o %t2.out +#RUN: FileCheck %s -check-prefix=CHECKGNULINKONCE < %t2.out.yaml +#RUN: llvm-readobj -sections %t2.out | FileCheck %s -check-prefix=CHECKGNULINKONCESECTIONS +#CHECKGNULINKONCE: - name: .gnu.linkonce.d.dummy +#CHECKGNULINKONCE: scope: global +#CHECKGNULINKONCE: type: gnu-linkonce +#CHECKGNULINKONCE: section-choice: custom-required +#CHECKGNULINKONCE: section-name: .gnu.linkonce.d.dummy +#CHECKGNULINKONCE: permissions: rw- +#CHECKGNULINKONCE: references: +#CHECKGNULINKONCE: - kind: group-child +#CHECKGNULINKONCE: offset: 0 +#CHECKGNULINKONCE: target: bar +#CHECKGNULINKONCE: - kind: group-child +#CHECKGNULINKONCE: offset: 0 +#CHECKGNULINKONCESECTIONS: Section { +#CHECKGNULINKONCESECTIONS: Name: .gnu.linkonce.d.dummy +#CHECKGNULINKONCESECTIONS: Type: SHT_PROGBITS +#CHECKGNULINKONCESECTIONS: Flags [ (0x3) +#CHECKGNULINKONCESECTIONS: SHF_ALLOC (0x2) +#CHECKGNULINKONCESECTIONS: SHF_WRITE (0x1) +#CHECKGNULINKONCESECTIONS: ] +#CHECKGNULINKONCESECTIONS: Size: 4 +#CHECKGNULINKONCESECTIONS: } +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .gnu.linkonce.d.dummy + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '00000000' +Symbols: + Local: + - Name: bar + Section: .gnu.linkonce.d.dummy + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .gnu.linkonce.d.dummy + Type: STT_SECTION + Section: .gnu.linkonce.d.dummy +... +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '00000000' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .gnu.linkonce.d.dummy + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '00000000' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .gnu.linkonce.d.dummy + Type: STT_SECTION + Section: .gnu.linkonce.d.dummy + - Name: foo + Section: .gnu.linkonce.d.dummy + Global: + - Name: main + Section: .text + - Name: start + Section: .text + - Name: _start + Section: .text + - Name: __start + Section: .text +... diff --git a/test/elf/gotpcrel.test b/test/elf/gotpcrel.test new file mode 100644 index 0000000000000..b6f83c16676d7 --- /dev/null +++ b/test/elf/gotpcrel.test @@ -0,0 +1,21 @@ +# This test checks that GOTPCREL entries are being handled properly +RUN: lld -flavor gnu -target x86_64-linux -static -e main --output-filetype=yaml \ +RUN: --noinhibit-exec %p/Inputs/gotpcrel.x86-64 \ +RUN: | FileCheck %s -check-prefix=YAML + +YAML: name: main +YAML: references: +YAML: kind: R_X86_64_GOTPCREL +YAML: offset: 3 +YAML: target: [[NULLGOT:[a-zA-Z0-9_]+]] +YAML: kind: R_X86_64_GOTPCREL +YAML: offset: 10 +YAML: target: [[MAINGOT:[a-zA-Z0-9_]+]] + +YAML: name: [[NULLGOT]] +YAML: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ] +YAML-NOT: references: + +YAML: name: [[MAINGOT]] +YAML: kind: R_X86_64_64 +YAML: target: main diff --git a/test/elf/gottpoff.test b/test/elf/gottpoff.test new file mode 100644 index 0000000000000..9841ee1453d04 --- /dev/null +++ b/test/elf/gottpoff.test @@ -0,0 +1,120 @@ +# Test that GOTTPOFF reloc generates an outstanding R_X86_64_TPOFF64 +# to be processed at startup time. +# Reference: Ulrich Drepper's "ELF Handling for Thread-Local storage" + +#RUN: yaml2obj -format=elf %s -o %t.o +#RUN: lld -flavor gnu -target x86_64 %t.o -o %t -e=main --defsym=__tls_get_addr=0 +#RUN: llvm-readobj -r %t | FileCheck %s +# +#CHECK: Section (5) .rela.dyn { +#CHECK: 0x401098 R_X86_64_TPOFF64 - 0x0 +#CHECK: } + +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_FREEBSD + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: E819000000640304250000000064030425000000006403042500000000C3488B0500000000648B00C3488D3D00000000E800000000488D8000000000C3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x0000000000000009 + Symbol: tls1 + Type: R_X86_64_TPOFF32 + - Offset: 0x0000000000000011 + Symbol: tls0 + Type: R_X86_64_TPOFF32 + - Offset: 0x0000000000000019 + Symbol: tls2 + Type: R_X86_64_TPOFF32 + - Offset: 0x0000000000000021 + Symbol: tls2 + Type: R_X86_64_GOTTPOFF + Addend: -4 + - Offset: 0x000000000000002C + Symbol: tls0 + Type: R_X86_64_TLSLD + Addend: -4 + - Offset: 0x0000000000000031 + Symbol: __tls_get_addr + Type: R_X86_64_PLT32 + Addend: -4 + - Offset: 0x0000000000000038 + Symbol: tls0 + Type: R_X86_64_DTPOFF32 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .tbss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 0x0000000000000004 + Content: '01000000002E7265' + - Name: .tdata + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + AddressAlign: 0x0000000000000004 + Content: '01000000' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .tbss + Type: STT_SECTION + Section: .tbss + - Name: .tdata + Type: STT_SECTION + Section: .tdata + Global: + - Name: GOTTPOFF + Type: STT_FUNC + Section: .text + Value: 0x000000000000001E + - Name: TLSLD + Type: STT_FUNC + Section: .text + Value: 0x0000000000000029 + - Name: main + Type: STT_FUNC + Section: .text + - Name: tls0 + Type: STT_TLS + Section: .tbss + Size: 0x0000000000000004 + - Name: tls1 + Type: STT_TLS + Section: .tbss + Value: 0x0000000000000004 + Size: 0x0000000000000004 + - Name: tls2 + Type: STT_TLS + Section: .tdata + Size: 0x0000000000000004 + - Name: _GLOBAL_OFFSET_TABLE_ + - Name: __tls_get_addr +... diff --git a/test/elf/group-cmd-search.test b/test/elf/group-cmd-search.test new file mode 100644 index 0000000000000..5e153c1ac1ad2 --- /dev/null +++ b/test/elf/group-cmd-search.test @@ -0,0 +1,134 @@ +/* + XFAIL: win32 + + This test does not pass on Windows because a path starting with + "/" is not considered as an absolute path. (It needs a drive + letter.) +*/ + +/* + In general the linker scripts's GROUP command works like a pair + of command line options --start-group/--end-group. But there is + a difference in the files look up algorithm. + + The --start-group/--end-group commands use a trivial approach: + a) If the path has '-l' prefix, add 'lib' prefix and '.a'/'.so' + suffix and search the path through library search directories. + b) Otherwise, use the path 'as-is'. + + The GROUP command implements more compicated approach: + a) If the path has '-l' prefix, add 'lib' prefix and '.a'/'.so' + suffix and search the path through library search directories. + b) If the path does not have '-l' prefix, and sysroot is configured, + and the path starts with the / character, and the script being + processed is located inside the sysroot, search the path under + the sysroot. Otherwise, try to open the path in the current + directory. If it is not found, search through library search + directories. +*/ + +/* + This link should finish successfully. The --start-group/--end-group + contains an existing absolute path to the file. + +RUN: lld -flavor gnu -target x86_64 -shared \ +RUN: -L%p/Inputs %p/Inputs/use-shared.x86-64 \ +RUN: --start-group %p/Inputs/shared.so-x86-64 --end-group -o %t1 +*/ + +/* + This link should fail with unknown input file format error. + There is no shared.so-x86-64 file in the current directory. + +RUN: not \ +RUN: lld -flavor gnu -target x86_64 -shared \ +RUN: -L%p/Inputs %p/Inputs/use-shared.x86-64 \ +RUN: --start-group shared.so-x86-64 --end-group -o %t2 +*/ + +/* + This link should fail with unknown input file format error. + The absolute path /shared.so-x86-64 does not exist and the linker + should not attempt to search it under the sysroot directory. + +RUN: not \ +RUN: lld -flavor gnu -target x86_64 -shared --sysroot=%p/Inputs \ +RUN: -L%p/Inputs %p/Inputs/use-shared.x86-64 \ +RUN: --start-group /shared.so-x86-64 --end-group -o %t3 +*/ + +/* + This link should finish successfully. The group-cmd-search-1.ls + script contains "GROUP ( shared.so-x86-64 )" command and the linker + has to search shared.so-x86-64 through the library search paths. + +RUN: lld -flavor gnu -target x86_64 -shared \ +RUN: -L%p/Inputs %p/Inputs/use-shared.x86-64 \ +RUN: %p/Inputs/group-cmd-search-1.ls -o %t4 +*/ + +/* + This link should fail with unknown input file format error. + The group-cmd-search-2.ls script contains GROUP command with + a non-existing absolute path but there is no --sysroot argument. + +RUN: not \ +RUN: lld -flavor gnu -target x86_64 -shared \ +RUN: -L%p/Inputs %p/Inputs/use-shared.x86-64 \ +RUN: %p/Inputs/group-cmd-search-2.ls -o %t5 +*/ + +/* + This link should finish successfully. The group-cmd-search-2.ls + script contains GROUP command with an absolute path and the sysroot + directory is provided. The linker has to search the absolute path + under the sysroot directory. + +RUN: lld -flavor gnu -target x86_64 -shared --sysroot=%p/Inputs \ +RUN: -L%p/Inputs %p/Inputs/use-shared.x86-64 \ +RUN: %p/Inputs/group-cmd-search-2.ls -o %t6 +*/ + +/* + This link should finish successfully. The group-cmd-search-2.ls + script contains GROUP command with an absolute path and the sysroot + directory is provided. The linker has to search the absolute path + under the sysroot directory. + +RUN: lld -flavor gnu -target x86_64 -shared --sysroot=%p/Inputs/../Inputs \ +RUN: -L%p/Inputs %p/Inputs/use-shared.x86-64 \ +RUN: %p/Inputs/group-cmd-search-2.ls -o %t6 +*/ + +/* + This link should finish successfully. The group-cmd-search-3.ls + script contains GROUP command with two elements. The first one + has a -l:<path> form and should be found by iterating through + lib dirs and searching the 'path' name exactly. The second element + has a -l<lib name> form and should be found by constructing a full + library name lib<lib name>.a and iterating through lib dirs. + +RUN: lld -flavor gnu -target x86_64 -shared \ +RUN: -L%p/Inputs %p/Inputs/use-shared.x86-64 \ +RUN: %p/Inputs/group-cmd-search-3.ls -o %t8 +*/ + +/* + This link should fail with unknown input file format error. + The linker script from this file contains GROUP with an absolute + path which can be found under provided sysroot directory. + But the linker script itself is not under the sysroot. + +RUN: not \ +RUN: lld -flavor gnu -target x86_64 -shared --sysroot=%p/Inputs \ +RUN: -L%p/Inputs %p/Inputs/use-shared.x86-64 \ +RUN: %s -o %t7 +*/ + +/* +RUN: lld -flavor gnu -target x86_64 -shared \ +RUN: -L%p/Inputs %p/Inputs/use-shared.x86-64 \ +RUN: -l:group-cmd-search-1.ls -o %t9 +*/ + +GROUP ( /shared.so-x86-64 ) diff --git a/test/elf/hexagon-quickdata-sort.test b/test/elf/hexagon-quickdata-sort.test new file mode 100644 index 0000000000000..efdf9480923d7 --- /dev/null +++ b/test/elf/hexagon-quickdata-sort.test @@ -0,0 +1,12 @@ +RUN: lld -flavor gnu -target hexagon %p/Inputs/quickdata-sort-test.o.elf-hexagon -o %t1 --noinhibit-exec +RUN: llvm-nm -n %t1 | FileCheck %s -check-prefix=quickdataSort + +quickdataSort: 00002000 D A1 +quickdataSort: 00002001 D AA1 +quickdataSort: 00002002 D B1 +quickdataSort: 00002004 D BB1 +quickdataSort: 00002008 D C1 +quickdataSort: 0000200c D CC1 +quickdataSort: 00002010 D D1 +quickdataSort: 00002018 D DD1 + diff --git a/test/elf/hexagon-quickdata-sortcommon.test b/test/elf/hexagon-quickdata-sortcommon.test new file mode 100644 index 0000000000000..5b4690b43cb97 --- /dev/null +++ b/test/elf/hexagon-quickdata-sortcommon.test @@ -0,0 +1,16 @@ +RUN: lld -flavor gnu -target hexagon -o %t1 --noinhibit-exec \ +RUN: %p/Inputs/quickdata-sortcommon-test.o.elf-hexagon +RUN: llvm-nm -n %t1 | FileCheck %s -check-prefix=quickdataSortCommon + +quickdataSortCommon: 00002000 D A1 +quickdataSortCommon: 00002001 D AA1 +quickdataSortCommon: 00002002 D AAA1 +quickdataSortCommon: 00002004 D B1 +quickdataSortCommon: 00002006 D BB1 +quickdataSortCommon: 00002008 D BBB1 +quickdataSortCommon: 0000200c D C1 +quickdataSortCommon: 00002010 D CC1 +quickdataSortCommon: 00002014 D CCC1 +quickdataSortCommon: 00002018 D D1 +quickdataSortCommon: 00002020 D DD1 +quickdataSortCommon: 00002028 D DDD1 diff --git a/test/elf/ifunc.test b/test/elf/ifunc.test new file mode 100644 index 0000000000000..c567c554cbbb0 --- /dev/null +++ b/test/elf/ifunc.test @@ -0,0 +1,69 @@ +# REQUIRES: x86 + +# This test checks that IRELATIVE relocations are created for symbols that +# need relocation even for static links. +RUN: lld -flavor gnu -target x86_64-linux --output-filetype=yaml -r \ +RUN: %p/Inputs/ifunc.x86-64 | FileCheck %s + +RUN: lld -flavor gnu -target x86_64-linux --output-filetype=yaml --noinhibit-exec \ +RUN: %p/Inputs/ifunc.x86-64 %p/Inputs/ifunc.cpp.x86-64 \ +RUN: | FileCheck %s --check-prefix=PLT + +RUN: lld -flavor gnu -target x86_64-linux -o %t %p/Inputs/ifunc.x86-64 \ +RUN: -e main -static %p/Inputs/ifunc.cpp.x86-64 +RUN: llvm-objdump -d -s %t| FileCheck %s --check-prefix=BIN +RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=RELATIVEADDEND + +# Test that STT_GNU_IFUNC symbols have type Code in SharedLibraryAtom. +RUN: lld -flavor gnu -target x86_64-linux --output-filetype=yaml \ +RUN: --noinhibit-exec %p/Inputs/ifunc.cpp.x86-64 -L%p/Inputs -lifunc.x86-64 \ +RUN: | FileCheck %s --check-prefix=SHARED + +PLT: defined-atoms: + +PLT: name: plt +PLT: scope: global +PLT: references: +PLT: kind: R_X86_64_PC32 +PLT: target: [[PLTNAME:[-a-zA-Z0-9_]+]] + +PLT: name: main +PLT: scope: global +PLT: references: +PLT: kind: R_X86_64_PC32 +PLT: target: [[PLTNAME]] + +// Make sure the target of main's relocation is a stub with a PC32 relocation. +// This relocation is to the got atom, but you can't really write that check in +// FileCheck. +PLT: name: +PLT: type: stub +PLT: references +PLT: kind: R_X86_64_PC32 + +// Make sure there's a got entry with a IRELATIVE relocation. +PLT: type: got +PLT: references: +PLT: kind: R_X86_64_IRELATIVE +PLT: target: hey + +CHECK: name: hey +CHECK: scope: global +CHECK: type: resolver + + +// This is a horribly brittle test. We need a way to do arithmetic on captured +// variables. +BIN: {{[0-9a-f]+}}: ff 25 {{[0-9a-f]+}} {{[0-9a-f]+}} 00 00 jmpq *{{[0-9]+}}(%rip) +BIN: .got.plt: +BIN-NEXT: {{[0-9a-f]+}} 00000000 00000000 + +RELATIVEADDEND: Relocations [ +RELATIVEADDEND-NEXT: Section (1) .rela.plt { +RELATIVEADDEND-NEXT: 0x401000 R_X86_64_IRELATIVE - 0x400110 +RELATIVEADDEND-NEXT: } +RELATIVEADDEND-NEXT: ] + +SHARED: shared-library-atoms +SHARED: name: hey +SHARED-NOT: data diff --git a/test/elf/ignore-unknownoption.test b/test/elf/ignore-unknownoption.test new file mode 100644 index 0000000000000..56856ed9f2e48 --- /dev/null +++ b/test/elf/ignore-unknownoption.test @@ -0,0 +1,5 @@ +# This test tests that lld is able to print unknown options that are not +# recognized. +RUN: not lld -flavor gnu -target x86_64 --gc-sections 2> %t +RUN: FileCheck %s < %t +CHECK: warning: ignoring unknown argument: --gc-sections diff --git a/test/elf/init_array-order.test b/test/elf/init_array-order.test new file mode 100644 index 0000000000000..b57b3807b69c8 --- /dev/null +++ b/test/elf/init_array-order.test @@ -0,0 +1,67 @@ +#RUN: yaml2obj -format=elf %s > %t +#RUN: lld -flavor gnu -target x86_64-linux %t --noinhibit-exec \ +#RUN: -o %t1.out +#RUN: llvm-objdump -s %t1.out | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: "1100000000000000" + AddressAlign: 8 + Flags: [SHF_ALLOC, SHF_EXECINSTR] +- Name: .init_array.2 + Type: SHT_INIT_ARRAY + Content: "0200000000000000" + AddressAlign: 8 + Flags: [SHF_ALLOC] +- Name: .init_array.3 + Type: SHT_INIT_ARRAY + Content: "0300000000000000" + AddressAlign: 8 + Flags: [SHF_ALLOC] +- Name: .init_array + Type: SHT_INIT_ARRAY + Content: "9900000000000000" + AddressAlign: 8 + Flags: [SHF_ALLOC] +- Name: .data + Type: SHT_PROGBITS + Content: "2200000000000000" + AddressAlign: 8 + Flags: [SHF_ALLOC, SHF_WRITE] +- Name: .init_array.1 + Type: SHT_INIT_ARRAY + Content: "0100000000000000" + AddressAlign: 8 + Flags: [SHF_ALLOC] + +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .init_array.3 + Type: STT_SECTION + Section: .init_array.3 + - Name: .init_array.2 + Type: STT_SECTION + Section: .init_array.2 + - Name: .init_array.1 + Type: STT_SECTION + Section: .init_array.1 + - Name: .init_array + Type: STT_SECTION + Section: .init_array + +#CHECK: {{[0xa-f0-9]+}} 01000000 00000000 02000000 00000000 +#CHECK: {{[0xa-f0-9]+}} 03000000 00000000 99000000 00000000 diff --git a/test/elf/init_array.test b/test/elf/init_array.test new file mode 100644 index 0000000000000..1acf4a7e7a282 --- /dev/null +++ b/test/elf/init_array.test @@ -0,0 +1,6 @@ +RUN: lld -flavor gnu -target x86_64-linux -o %t %p/Inputs/init_array.x86-64 \ +RUN: -e __init_array_start +RUN: llvm-objdump -t -section-headers %t | FileCheck %s + +CHECK: .init_array {{[0-9]+}} [[ADDR:[0-9]+]] +CHECK: [[ADDR]] g *ABS* {{[0-9]+}} __init_array_start diff --git a/test/elf/initfini-options.test-1.test b/test/elf/initfini-options.test-1.test new file mode 100644 index 0000000000000..2fc1c3e32bda5 --- /dev/null +++ b/test/elf/initfini-options.test-1.test @@ -0,0 +1,33 @@ +# Check that if there are no -init/-fini options and _init/_fini symbols +# are undefined the linker does not emit DT_INIT/DT_FINI tags. + +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target x86_64 -shared --noinhibit-exec -o %t.so %t.o +# RUN: llvm-readobj -dynamic-table %t.so | FileCheck %s + +# CHECK-NOT: 0x000000000000000C INIT 0x{{[0-9A-F]+}} +# CHECK-NOT: 0x000000000000000D FINI 0x{{[0-9A-F]+}} + +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x08 + +Symbols: + Global: + - Name: _start + Type: STT_FUNC + Section: .text + Size: 0x08 + - Name: _init + - Name: _fini +... diff --git a/test/elf/initfini-options.test-2.test b/test/elf/initfini-options.test-2.test new file mode 100644 index 0000000000000..4742084c513bf --- /dev/null +++ b/test/elf/initfini-options.test-2.test @@ -0,0 +1,47 @@ +# Check that if _init/_fini symbols are defined the linker emits +# DT_INIT/DT_FINI tags point to these symbols. + +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target x86_64 -shared -o %t.so %t.o +# RUN: llvm-readobj -symbols -dynamic-table %t.so | FileCheck %s + +# CHECK: Name: _init (8) +# CHECK-NEXT: Value: {{[0x0-9a-f]+}} +# CHECK: Name: _fini (14) +# CHECK-NEXT: Value: {{[0x0-9a-f]+}} +# +# CHECK: 0x000000000000000C INIT {{[0x0-9a-f]+}} +# CHECK: 0x000000000000000D FINI {{[0x0-9a-f]+}} + +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x18 + +Symbols: + Global: + - Name: _start + Type: STT_FUNC + Section: .text + Value: 0x0 + Size: 0x8 + - Name: _init + Type: STT_FUNC + Section: .text + Value: 0x8 + Size: 0x8 + - Name: _fini + Type: STT_FUNC + Section: .text + Value: 0xF + Size: 0x8 +... diff --git a/test/elf/initfini-options.test-3.test b/test/elf/initfini-options.test-3.test new file mode 100644 index 0000000000000..bf8b216775d9a --- /dev/null +++ b/test/elf/initfini-options.test-3.test @@ -0,0 +1,53 @@ +# Check that -init/-fini command line options override default function names +# and the linker uses these name to search symbols and setup DT_INIT/DT_FINI. + +# RUN: yaml2obj -format=elf %s > %t.o +# RUN: lld -flavor gnu -target x86_64 -shared -o %t.so %t.o \ +# RUN: -init _init -init _start -fini _fini -fini _stop +# RUN: llvm-readobj -symbols -dynamic-table %t.so | FileCheck %s + +# CHECK: Name: _start (1) +# CHECK-NEXT: Value: {{[0x0-9a-f]+}} +# CHECK: Name: _stop (8) +# CHECK-NEXT: Value: {{[0x0-9a-f]+}} +# +# CHECK: 0x000000000000000C INIT {{[0x0-9a-f]+}} +# CHECK: 0x000000000000000D FINI {{[0x0-9a-f]+}} + +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 + +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x04 + Size: 0x20 + +Symbols: + Global: + - Name: _start + Type: STT_FUNC + Section: .text + Value: 0x0 + Size: 0x8 + - Name: _stop + Type: STT_FUNC + Section: .text + Value: 0x8 + Size: 0x8 + - Name: _init + Type: STT_FUNC + Section: .text + Value: 0xF + Size: 0x8 + - Name: _fini + Type: STT_FUNC + Section: .text + Value: 0x18 + Size: 0x8 +... diff --git a/test/elf/librarynotfound.test b/test/elf/librarynotfound.test new file mode 100644 index 0000000000000..faa1728b478bf --- /dev/null +++ b/test/elf/librarynotfound.test @@ -0,0 +1,5 @@ +# Tests the functionality of library not found +RUN: not lld -flavor gnu -lfn 2> %t1 +RUN: FileCheck %s < %t1 + +CHECK: Unable to find library -lfn diff --git a/test/elf/linker-as-ld.test b/test/elf/linker-as-ld.test new file mode 100644 index 0000000000000..8ac75275f996c --- /dev/null +++ b/test/elf/linker-as-ld.test @@ -0,0 +1,16 @@ +REQUIRES: system-linker-elf + +RUN: mkdir -p %t.dir && cp `which lld` %t.dir/ld +RUN: %t.dir/ld -target x86_64-linux -o %t %p/Inputs/relocs.x86-64 \ +RUN: -e _start -static +RUN: llvm-readobj -t %t | FileCheck %s + +# Test linker run as "ld" on elf based system works like gnu linker. + + +CHECK: Symbol { +CHECK: Name: i +CHECK-NEXT: Value: +CHECK-NEXT: Size: +CHECK-NEXT: Binding: +CHECK-NEXT: Type: Object diff --git a/test/elf/linkerscript/Inputs/externs.ls b/test/elf/linkerscript/Inputs/externs.ls new file mode 100644 index 0000000000000..20fdc0c3f9803 --- /dev/null +++ b/test/elf/linkerscript/Inputs/externs.ls @@ -0,0 +1,3 @@ +/* A simple valid linker script used for testing the EXTERN command. + */ +EXTERN(_foo bar __baz) diff --git a/test/elf/linkerscript/Inputs/invalid.ls b/test/elf/linkerscript/Inputs/invalid.ls new file mode 100644 index 0000000000000..894d4bef2732c --- /dev/null +++ b/test/elf/linkerscript/Inputs/invalid.ls @@ -0,0 +1 @@ +GROUP( diff --git a/test/elf/linkerscript/Inputs/prog1.o.yaml b/test/elf/linkerscript/Inputs/prog1.o.yaml new file mode 100644 index 0000000000000..ded590e395d43 --- /dev/null +++ b/test/elf/linkerscript/Inputs/prog1.o.yaml @@ -0,0 +1,88 @@ +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E5B000E800000000BF01000000BA0E0000004889C6E80000000031C05DC3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x0000000000000007 + Symbol: prog2 + Type: R_X86_64_PC32 + Addend: -4 + - Offset: 0x0000000000000019 + Symbol: write + Type: R_X86_64_PC32 + Addend: -4 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 00636C616E672076657273696F6E20332E372E302028687474703A2F2F6C6C766D2E6F72672F6769742F636C616E672E6769742036336134646334616430343938646139623934386330383263623735336430353735323938346638292028687474703A2F2F6C6C766D2E6F72672F6769742F6C6C766D2E67697420623838363135326664656538376564653738613565643965616638663664313839343033616266312900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: 1400000000000000037A5200017810011B0C0708900100001C0000001C000000000000002100000000410E108602430D0600000000000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .eh_frame + Relocations: + - Offset: 0x0000000000000020 + Symbol: .text + Type: R_X86_64_PC32 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .comment + Type: STT_SECTION + Section: .comment + - Name: .note.GNU-stack + Type: STT_SECTION + Section: .note.GNU-stack + - Name: .eh_frame + Type: STT_SECTION + Section: .eh_frame + Global: + - Name: main + Type: STT_FUNC + Section: .text + Size: 0x0000000000000021 + - Name: prog2 + - Name: write +... diff --git a/test/elf/linkerscript/Inputs/prog2.o.yaml b/test/elf/linkerscript/Inputs/prog2.o.yaml new file mode 100644 index 0000000000000..f88b0ddc96b22 --- /dev/null +++ b/test/elf/linkerscript/Inputs/prog2.o.yaml @@ -0,0 +1,89 @@ +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E548B800000000000000005DC3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x0000000000000006 + Symbol: .rodata.str1.1 + Type: R_X86_64_64 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .rodata.str1.1 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 48656C6C6F2C20776F726C64210A00 + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 00636C616E672076657273696F6E20332E372E302028687474703A2F2F6C6C766D2E6F72672F6769742F636C616E672E6769742036336134646334616430343938646139623934386330383263623735336430353735323938346638292028687474703A2F2F6C6C766D2E6F72672F6769742F6C6C766D2E67697420623838363135326664656538376564653738613565643965616638663664313839343033616266312900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: 1400000000000000037A5200017810011B0C0708900100001C0000001C000000000000001000000000410E108602430D0600000000000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .eh_frame + Relocations: + - Offset: 0x0000000000000020 + Symbol: .text + Type: R_X86_64_PC32 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .rodata.str1.1 + Type: STT_SECTION + Section: .rodata.str1.1 + - Name: .comment + Type: STT_SECTION + Section: .comment + - Name: .note.GNU-stack + Type: STT_SECTION + Section: .note.GNU-stack + - Name: .eh_frame + Type: STT_SECTION + Section: .eh_frame + Global: + - Name: prog2 + Type: STT_FUNC + Section: .text + Size: 0x0000000000000010 +... diff --git a/test/elf/linkerscript/Inputs/prog3.o.yaml b/test/elf/linkerscript/Inputs/prog3.o.yaml new file mode 100644 index 0000000000000..76aa22267f0b6 --- /dev/null +++ b/test/elf/linkerscript/Inputs/prog3.o.yaml @@ -0,0 +1,52 @@ +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: B8010000000F05C3E800000000B83C0000000F05C3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x0000000000000009 + Symbol: main + Type: R_X86_64_PC32 + Addend: -4 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + Global: + - Name: _start + Section: .text + Value: 0x0000000000000008 + - Name: write + Section: .text + - Name: main +... diff --git a/test/elf/linkerscript/Inputs/simple.o.yaml b/test/elf/linkerscript/Inputs/simple.o.yaml new file mode 100644 index 0000000000000..91d4e1b577869 --- /dev/null +++ b/test/elf/linkerscript/Inputs/simple.o.yaml @@ -0,0 +1,52 @@ +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: B80100000048C7C70100000048C7C60000000048C7C20E0000000F05C3E8DEFFFFFFB83C0000000F05C3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x000000000000000F + Symbol: .data + Type: R_X86_64_32S + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: 48656C6C6F2C20576F726C64210A00 + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' +Symbols: + Local: + - Name: main + Section: .text + - Name: msg + Section: .data + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + Global: + - Name: _start + Section: .text + Value: 0x000000000000001D +... diff --git a/test/elf/linkerscript/Inputs/valid.ls b/test/elf/linkerscript/Inputs/valid.ls new file mode 100644 index 0000000000000..43593602d3fbf --- /dev/null +++ b/test/elf/linkerscript/Inputs/valid.ls @@ -0,0 +1,6 @@ +/* A simple valid linker script used for testing the -T/--script options. + * + * An unresolved symbol named '_entry_point' can be scanned for by the tests + * to determine that the linker script was processed. + */ +ENTRY(_entry_point) diff --git a/test/elf/linkerscript/externs.objtxt b/test/elf/linkerscript/externs.objtxt new file mode 100644 index 0000000000000..154891eaf7107 --- /dev/null +++ b/test/elf/linkerscript/externs.objtxt @@ -0,0 +1,21 @@ +# Check symbols defined with the EXTERN command are added as undefined +# symbols. + +# RUN: lld -flavor gnu -target x86_64 -T %p/Inputs/externs.ls -r %s \ +# RUN: --output-filetype=yaml | FileCheck %s + +defined-atoms: + - name: main + scope: global + content: [ B8, 00, 00, 00, 00, C7, 44, 24, FC, 00, 00, 00, 00, C3 ] + alignment: 2^4 + section-choice: custom-required + section-name: .text + +# CHECK: undefined-atoms: +# CHECK: - name: _foo +# CHECK: can-be-null: at-buildtime +# CHECK: - name: bar +# CHECK: can-be-null: at-buildtime +# CHECK: - name: __baz +# CHECK: can-be-null: at-buildtime diff --git a/test/elf/linkerscript/invalid-script-cli-1.test b/test/elf/linkerscript/invalid-script-cli-1.test new file mode 100644 index 0000000000000..904ba17557c0c --- /dev/null +++ b/test/elf/linkerscript/invalid-script-cli-1.test @@ -0,0 +1,10 @@ +# Check that the -T/--script options issue an error when passed +# filenames for files that do not exist. + +RUN: not lld -flavor gnu -target x86_64 -T idonotexist.ls 2> %t.err +RUN: FileCheck %s < %t.err + +RUN: not lld -flavor gnu -target x86_64 --script=idonotexist.ls 2> %t.err +RUN: FileCheck %s < %t.err + +CHECK: {{.*}}lld: cannot find file {{.*}}idonotexist.ls diff --git a/test/elf/linkerscript/invalid-script-cli-2.test b/test/elf/linkerscript/invalid-script-cli-2.test new file mode 100644 index 0000000000000..6e0e42adc71d6 --- /dev/null +++ b/test/elf/linkerscript/invalid-script-cli-2.test @@ -0,0 +1,6 @@ +# Check that linker script are *not* picked up with -lscript.ls. + +RUN: not lld -flavor gnu -target x86_64 -L%p/Inputs/ -lvalid.ls 2> %t.err +RUN: FileCheck %s < %t.err + +CHECK: {{.*}}: Unable to find library -lvalid.ls diff --git a/test/elf/linkerscript/invalid.test b/test/elf/linkerscript/invalid.test new file mode 100644 index 0000000000000..42833a6664ffb --- /dev/null +++ b/test/elf/linkerscript/invalid.test @@ -0,0 +1,5 @@ +# Check for errors from invalid linker scripts +RUN: not lld -flavor gnu -target x86_64 %p/Inputs/invalid.ls 2> %t.err +RUN: FileCheck %s < %t.err + +CHECK: {{.*}}invalid.ls: Error parsing linker script diff --git a/test/elf/linkerscript/sections-order.test b/test/elf/linkerscript/sections-order.test new file mode 100644 index 0000000000000..85a172cb07adc --- /dev/null +++ b/test/elf/linkerscript/sections-order.test @@ -0,0 +1,97 @@ +/* +Tests a simple linker script that changes the order of output sections and +also changes the address of output sections by using simple expressions. + +This test uses three X86-64 input objects, prog1.o, prog2.o and prog3.o, +which were created with the following C or assembly code: + +*** prog1.o: + +(command line clang -c prog1.c -o prog1.o) + +const char *prog2(); +void write(int, const char *, int); + +int main() { + write(1, prog2(), 14); +} + +*** prog2.o: + +(command line clang -c prog2.c -o prog2.o) + +const char *prog2() { + return "Hello, world!\n"; +} + +*** prog3.o: + +(command line clang -c prog3.S -o prog3.o) + + .globl write +write: + mov $1, %eax + syscall + ret + + .globl _start +_start: + call main + mov $60, %eax + syscall + ret + +We use the following linker script for this test: +*/ + +ENTRY(_start) + +SECTIONS +{ + . = 0x500000; + .text : { prog1.o(.text) } + .mystring : { prog2.o(.rodata.str1.1) } + . = . + 0x6000; + .text.2 : {prog3.o(.text) prog2.o(.text) } +} + +/* +RUN: mkdir -p %T +RUN: yaml2obj -format=elf %p/Inputs/prog1.o.yaml -o=%T/prog1.o +RUN: yaml2obj -format=elf %p/Inputs/prog2.o.yaml -o=%T/prog2.o +RUN: yaml2obj -format=elf %p/Inputs/prog3.o.yaml -o=%T/prog3.o +RUN: cd %T + +RUN: lld -flavor gnu -target x86_64 -T %s prog1.o prog2.o prog3.o \ +RUN: -static -o %t1 +RUN: llvm-readobj -s %t1 | FileCheck -check-prefix CHECKSECTIONS %s + +CHECKSECTIONS: Index: 1 +CHECKSECTIONS: Name: .text +CHECKSECTIONS: Address: 0x500000 +CHECKSECTIONS: Size: 33 + +CHECKSECTIONS: Index: 2 +CHECKSECTIONS: Name: .mystring +CHECKSECTIONS: Address: 0x500021 +CHECKSECTIONS: Size: 15 + +CHECKSECTIONS: Index: 3 +CHECKSECTIONS: Name: .text.2 +CHECKSECTIONS: Address: 0x506030 +CHECKSECTIONS: Size: 48 + +RUN: llvm-readobj -symbols %t1 | FileCheck -check-prefix CHECKSYMS %s + +CHECKSYMS: Name: main +CHECKSYMS-NEXT: Value: 0x500000 + +CHECKSYMS: Name: write +CHECKSYMS-NEXT: Value: 0x506030 + +CHECKSYMS: Name: _start +CHECKSYMS-NEXT: Value: 0x506038 + +CHECKSYMS: Name: prog2 +CHECKSYMS-NEXT: Value: 0x506050 +*/ diff --git a/test/elf/linkerscript/sections-with-wildcards.test b/test/elf/linkerscript/sections-with-wildcards.test new file mode 100644 index 0000000000000..6af80d0883dea --- /dev/null +++ b/test/elf/linkerscript/sections-with-wildcards.test @@ -0,0 +1,88 @@ +/* +Tests a linker script that uses the SECTIONS command with rules containing +wildcards and simple SORT directives. It also tests that the linker script +evaluates the expressions in the same order as the one written in the script +file. + +This test uses three X86-64 input objects, prog1.o, prog2.o and prog3.o, +which were created with the following C or assembly code: + +*** prog1.o: + +(command line clang -c prog1.c -o prog1.o) + +const char *prog2(); +void write(int, const char *, int); + +int main() { + write(1, prog2(), 14); +} + +*** prog2.o: + +(command line clang -c prog2.c -o prog2.o) + +const char *prog2() { + return "Hello, world!\n"; +} + +*** prog3.o: + +(command line clang -c prog3.S -o prog3.o) + + .globl write +write: + mov $1, %eax + syscall + ret + + .globl _start +_start: + call main + mov $60, %eax + syscall + ret + +We use the following linker script for this test: +*/ + +ENTRY(_start) + +SECTIONS +{ + my_start_addr = 0x500000; + my_symbol = my_start_addr; + . = my_symbol; + .foo : { SORT(*)(.text .rodata*) } +} + +/* +RUN: mkdir -p %T +RUN: yaml2obj -format=elf %p/Inputs/prog1.o.yaml -o=%T/p1.o +RUN: yaml2obj -format=elf %p/Inputs/prog2.o.yaml -o=%T/p2.o +RUN: yaml2obj -format=elf %p/Inputs/prog3.o.yaml -o=%T/p3.o +RUN: cd %T + +RUN: lld -flavor gnu -target x86_64 -T %s p1.o p2.o p3.o \ +RUN: -static -o %t1 +RUN: llvm-readobj -s %t1 | FileCheck -check-prefix CHECKSECTIONS %s + +CHECKSECTIONS: Index: 1 +CHECKSECTIONS: Name: .foo +CHECKSECTIONS: Address: 0x500000 +CHECKSECTIONS: Size: 101 + +RUN: llvm-readobj -symbols %t1 | FileCheck -check-prefix CHECKSYMS %s + +CHECKSYMS: Name: main +CHECKSYMS-NEXT: Value: 0x500000 + +CHECKSYMS: Name: prog2 +CHECKSYMS-NEXT: Value: 0x500030 + +CHECKSYMS: Name: write +CHECKSYMS-NEXT: Value: 0x500050 + +CHECKSYMS: Name: _start +CHECKSYMS-NEXT: Value: 0x500058 +*/ diff --git a/test/elf/linkerscript/symbol-definition.test b/test/elf/linkerscript/symbol-definition.test new file mode 100644 index 0000000000000..fc595bbe1f105 --- /dev/null +++ b/test/elf/linkerscript/symbol-definition.test @@ -0,0 +1,54 @@ +/* +We test whether we can define symbols in a linker script and have them exported +to the output file symbol table. + +This test uses a single X86-64 input object, simple.o, created with the +following X86-64 assembly code: + +*** simple.S: + +(command line clang -c simple.S -o simple.o) + + .text + main: + mov $1, %eax + movq $1, %rdi + movq $msg, %rsi + movq $14, %rdx + syscall + ret + + .globl _start + _start: + call main + mov $60, %eax + syscall + ret + + .data + msg: .asciz "Hello, World!\n" + + +We use the following linker script for this test: +*/ + +ENTRY(_start) + +SECTIONS +{ + . = 0x500000; + .text : { *(.text) } + MYSTRING = .; + .data : { *(.data) } +} + +/* +RUN: mkdir -p %T +RUN: yaml2obj -format=elf %p/Inputs/simple.o.yaml -o=%T/simple.o + +RUN: lld -flavor gnu -target x86_64 -T %s %T/simple.o -static -o %t1 +RUN: llvm-readobj -symbols %t1 | FileCheck -check-prefix CHECKSYMS %s + +CHECKSYMS: Name: MYSTRING +CHECKSYMS-NEXT: Value: 0x501000 +*/ diff --git a/test/elf/linkerscript/valid-script-cli.objtxt b/test/elf/linkerscript/valid-script-cli.objtxt new file mode 100644 index 0000000000000..b68d430fd98ed --- /dev/null +++ b/test/elf/linkerscript/valid-script-cli.objtxt @@ -0,0 +1,23 @@ +# Check that the linker script inputs are accepted properly. + +# RUN: lld -flavor gnu -target x86_64 %p/Inputs/valid.ls -r %s \ +# RUN: --output-filetype=yaml | FileCheck %s + +# RUN: lld -flavor gnu -target x86_64 -T %p/Inputs/valid.ls -r %s \ +# RUN: --output-filetype=yaml | FileCheck %s + +# RUN: lld -flavor gnu -target x86_64 --script=%p/Inputs/valid.ls -r %s \ +# RUN: --output-filetype=yaml | FileCheck %s + +# RUN: lld -flavor gnu -target x86_64 -L%p/Inputs/ -l:valid.ls -r %s \ +# RUN: --output-filetype=yaml | FileCheck %s + +defined-atoms: + - name: main + scope: global + content: [ B8, 00, 00, 00, 00, C7, 44, 24, FC, 00, 00, 00, 00, C3 ] + alignment: 2^4 + section-choice: custom-required + section-name: .text + +# CHECK: _entry_point diff --git a/test/elf/loginputfiles.test b/test/elf/loginputfiles.test new file mode 100644 index 0000000000000..850570d8085e1 --- /dev/null +++ b/test/elf/loginputfiles.test @@ -0,0 +1,28 @@ +# Tests functionality of -t +# +# Tests generated using the source files below +# main file +# int main() +# { +# fn(); +# return 0; +# } +# +# archive file +# int fn() +# { +# return 0; +# } +# +# int fn1() +# { +# return 0; +# } +# gcc -c main.c fn.c fn1.c + +RUN: lld -flavor gnu -target x86_64-linux \ +RUN: %p/Inputs/mainobj.x86_64 %p/Inputs/libfnarchive.a -t --noinhibit-exec 2>&1 | \ +RUN: FileCheck -check-prefix INPUTFILES %s + +#INPUTFILES: mainobj.x86_64 +#INPUTFILES: libfnarchive.a(fn.o) diff --git a/test/elf/mergeatoms.test b/test/elf/mergeatoms.test new file mode 100644 index 0000000000000..521eb5a12c3c3 --- /dev/null +++ b/test/elf/mergeatoms.test @@ -0,0 +1,6 @@ +# Tests that atoms are merged by testing it with --merge-strings option +RUN: lld -flavor gnu -target x86_64-linux --merge-strings -o %t1 \ +RUN: %p/Inputs/foo.o.x86-64 %p/Inputs/bar.o.x86-64 -e bar1 +RUN: llvm-objdump -s %t1 | FileCheck -check-prefix=mergeAtoms %s + +mergeAtoms: 62617200 666f6f00 bar.foo. diff --git a/test/elf/mergeconstants.test b/test/elf/mergeconstants.test new file mode 100644 index 0000000000000..3d06d2c944383 --- /dev/null +++ b/test/elf/mergeconstants.test @@ -0,0 +1,20 @@ +# The test checks for mergeable strings that appear in the object file +RUN: lld -flavor gnu --merge-strings --output-filetype=yaml -target x86_64 \ +RUN: %p/Inputs/constants-merge.x86-64 --noinhibit-exec \ +RUN: | FileCheck -check-prefix=mergeAtoms %s + +mergeAtoms: - ref-name: [[CONSTANT:[-a-zA-Z0-9_]+]] +mergeAtoms: type: constant +mergeAtoms: content: [ 62, 61, 72, 66, 6F, 6F, 00 ] +mergeAtoms: merge: by-content +mergeAtoms: section-choice: custom-required +mergeAtoms: section-name: .rodata.str1.1 +mergeAtoms: - name: foo +mergeAtoms: scope: global +mergeAtoms: type: data +mergeAtoms: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ] +mergeAtoms: alignment: 2^3 +mergeAtoms: references: +mergeAtoms: - kind: R_X86_64_64 +mergeAtoms: offset: 3 +mergeAtoms: target: [[CONSTANT]] diff --git a/test/elf/mergeglobalatoms.test b/test/elf/mergeglobalatoms.test new file mode 100644 index 0000000000000..e71dca539915c --- /dev/null +++ b/test/elf/mergeglobalatoms.test @@ -0,0 +1,11 @@ +# ELF files can have mergeable strings which are global!, treat them as global +# defined atoms +RUN: lld -flavor gnu --output-filetype=yaml %p/Inputs/globalconst.o.x86-64 \ +RUN: --noinhibit-exec -target x86_64 | FileCheck -check-prefix=globalatoms %s + +globalatoms: - name: mystr +globalatoms: scope: global +globalatoms: type: constant +globalatoms: content: [ 66, 6F, 6F, 62, 61, 72, 00 ] +globalatoms: section-choice: custom-required +globalatoms: section-name: .rodata.str1.1 diff --git a/test/elf/note.test b/test/elf/note.test new file mode 100644 index 0000000000000..f0e9c6b2f8d9a --- /dev/null +++ b/test/elf/note.test @@ -0,0 +1,49 @@ +# Check that the linker is not ignoring input sections. +# RUN: yaml2obj -format=elf %s > %t.obj +# RUN: lld -flavor gnu -target x86_64 %t.obj -o %t.exe --noinhibit-exec +# RUN: llvm-objdump -h %t.exe | FileCheck %s + +# CHECK: {{[0-9]+}} .note + +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .note + Type: SHT_NOTE + AddressAlign: 0x0000000000000001 + Content: '00' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .note + Type: STT_SECTION + Section: .note +... diff --git a/test/elf/options/dynamic-linker.test b/test/elf/options/dynamic-linker.test new file mode 100644 index 0000000000000..3d8feeb6e3076 --- /dev/null +++ b/test/elf/options/dynamic-linker.test @@ -0,0 +1,17 @@ +# This tests the functionality of specifying dynamic-linker argument in the +# command line +RUN: lld -flavor gnu -target x86_64 --dynamic-linker="/xyz.so" \ +RUN: %p/../Inputs/foo.o.x86-64 --noinhibit-exec -o %t +RUN: llvm-objdump -s %t | FileCheck -check-prefix=DYNAMICINTERP1 %s +RUN: lld -flavor gnu -target x86_64 --dynamic-linker="" \ +RUN: %p/../Inputs/foo.o.x86-64 --noinhibit-exec -o %t1 +RUN: llvm-objdump -s %t1 | FileCheck -check-prefix=DYNAMICINTERP2 %s +RUN: lld -flavor gnu -target x86_64 -dynamic-linker /xyz.so \ +RUN: %p/../Inputs/foo.o.x86-64 --noinhibit-exec -o %t2 +RUN: llvm-objdump -s %t2 | FileCheck -check-prefix=DYNAMICINTERP1 %s + +DYNAMICINTERP1:Contents of section .interp: +DYNAMICINTERP1: 400158 2f78797a 2e736f00 /xyz.so. +DYNAMICINTERP2:Contents of section .interp: +DYNAMICINTERP2: 400158 00 + diff --git a/test/elf/phdr.test b/test/elf/phdr.test new file mode 100644 index 0000000000000..c8ab73d31464c --- /dev/null +++ b/test/elf/phdr.test @@ -0,0 +1,99 @@ +# This test checks emission for program header for ELF binaries +RUN: lld -flavor gnu -target i386-linux -o %t1 -e main %p/Inputs/phdr.i386 \ +RUN: && llvm-readobj -program-headers %t1 | FileCheck -check-prefix=I386 %s +RUN: lld -flavor gnu -target x86_64-linux -o %t1 -e _start %p/Inputs/relocs.x86-64 -static \ +RUN: && llvm-objdump -p %t1 | FileCheck %s -check-prefix=X86_64 + + +I386: ProgramHeaders [ +I386-NEXT: ProgramHeader { +I386-NEXT: Type: PT_PHDR (0x6) +I386-NEXT: Offset: 0x34 +I386-NEXT: VirtualAddress: 0x34 +I386-NEXT: PhysicalAddress: 0x34 +I386-NEXT: FileSize: 224 +I386-NEXT: MemSize: 224 +I386-NEXT: Flags [ (0x5) +I386-NEXT: PF_R (0x4) +I386-NEXT: PF_X (0x1) +I386-NEXT: ] +I386-NEXT: Alignment: 8 +I386-NEXT: } +I386-NEXT: ProgramHeader { +I386-NEXT: Type: PT_INTERP (0x3) +I386-NEXT: Offset: 0x114 +I386-NEXT: VirtualAddress: 0x114 +I386-NEXT: PhysicalAddress: 0x114 +I386-NEXT: FileSize: 28 +I386-NEXT: MemSize: 28 +I386-NEXT: Flags [ (0x4) +I386-NEXT: PF_R (0x4) +I386-NEXT: ] +I386-NEXT: Alignment: 1 +I386-NEXT: } +I386-NEXT: ProgramHeader { +I386-NEXT: Type: PT_LOAD (0x1) +I386-NEXT: Offset: 0x0 +I386-NEXT: VirtualAddress: 0x0 +I386-NEXT: PhysicalAddress: 0x0 +I386-NEXT: FileSize: 556 +I386-NEXT: MemSize: 556 +I386-NEXT: Flags [ (0x5) +I386-NEXT: PF_R (0x4) +I386-NEXT: PF_X (0x1) +I386-NEXT: ] +I386-NEXT: Alignment: 4096 +I386-NEXT: } +I386-NEXT: ProgramHeader { +I386-NEXT: Type: PT_LOAD (0x1) +I386-NEXT: Offset: 0x1000 +I386-NEXT: VirtualAddress: 0x1000 +I386-NEXT: PhysicalAddress: 0x1000 +I386-NEXT: FileSize: 260 +I386-NEXT: MemSize: 260 +I386-NEXT: Flags [ (0x6) +I386-NEXT: PF_R (0x4) +I386-NEXT: PF_W (0x2) +I386-NEXT: ] +I386-NEXT: Alignment: 4096 +I386-NEXT: } +I386-NEXT: ProgramHeader { +I386-NEXT: Type: PT_LOAD (0x1) +I386-NEXT: Offset: 0x4000 +I386-NEXT: VirtualAddress: 0x4000 +I386-NEXT: PhysicalAddress: 0x4000 +I386-NEXT: FileSize: 4 +I386-NEXT: MemSize: 8 +I386-NEXT: Flags [ (0x6) +I386-NEXT: PF_R (0x4) +I386-NEXT: PF_W (0x2) +I386-NEXT: ] +I386-NEXT: Alignment: 16384 +I386-NEXT: } +I386-NEXT: ProgramHeader { +I386-NEXT: Type: PT_DYNAMIC (0x2) +I386-NEXT: Offset: 0x1FC +I386-NEXT: VirtualAddress: 0x1FC +I386-NEXT: PhysicalAddress: 0x1FC +I386-NEXT: FileSize: 48 +I386-NEXT: MemSize: 48 +I386-NEXT: Flags [ (0x4) +I386-NEXT: PF_R (0x4) +I386-NEXT: ] +I386-NEXT: Alignment: 4 +I386-NEXT: } +I386-NEXT: ProgramHeader { +I386-NEXT: Type: PT_GNU_EH_FRAME (0x6474E550) +I386-NEXT: Offset: 0x1F4 +I386-NEXT: VirtualAddress: 0x1F4 +I386-NEXT: PhysicalAddress: 0x1F4 +I386-NEXT: FileSize: 8 +I386-NEXT: MemSize: 8 +I386-NEXT: Flags [ (0x4) +I386-NEXT: PF_R (0x4) +I386-NEXT: ] +I386-NEXT: Alignment: 4 +I386-NEXT: } + +X86_64: LOAD off 0x0000000000000000 +X86_64: LOAD off 0x0000000000001000 diff --git a/test/elf/quickdata.test b/test/elf/quickdata.test new file mode 100644 index 0000000000000..a07771517226b --- /dev/null +++ b/test/elf/quickdata.test @@ -0,0 +1,15 @@ +RUN: lld -flavor gnu -target hexagon --output-filetype=yaml %p/Inputs/quickdata-test.elf-hexagon \ +RUN: --noinhibit-exec | FileCheck %s -check-prefix hexagon + +hexagon: - name: ac1 +hexagon: scope: global +hexagon: type: zero-fill-quick +hexagon: size: 1 +hexagon: merge: as-tentative +hexagon: - name: init +hexagon: scope: global +hexagon: type: quick-data +hexagon: - name: bss1 +hexagon: scope: global +hexagon: type: zero-fill-quick + diff --git a/test/elf/reloc.test b/test/elf/reloc.test new file mode 100644 index 0000000000000..0ecf0b174fe80 --- /dev/null +++ b/test/elf/reloc.test @@ -0,0 +1,38 @@ +RUN: lld -flavor gnu -target i386 --merge-strings -r --output-filetype=yaml \ +RUN: %p/Inputs/reloc-test.elf-i386 | FileCheck %s -check-prefix ELF-i386 + +ELF-i386: defined-atoms: +ELF-i386: - ref-name: [[STRNAMEA:[-a-zA-Z0-9_]+]] +ELF-i386: type: constant +ELF-i386: content: [ 68, 65, 6C, 6C, 6F, 20, 77, 6F, 72, 6C, 64, 00 ] +ELF-i386: merge: by-content +ELF-i386: - ref-name: [[STRNAMEB:[-a-zA-Z0-9_]+]] +ELF-i386: alignment: 2^4 +ELF-i386: section-choice: custom-required +ELF-i386: section-name: .text.startup +ELF-i386: references: +ELF-i386: - kind: layout-after +ELF-i386: offset: 0 +ELF-i386: target: main +ELF-i386: - name: main +ELF-i386: scope: global +ELF-i386: content: [ 55, 89, E5, 83, E4, F0, 83, EC, 10, C7, 04, 24, +ELF-i386: 00, 00, 00, 00, E8, FC, FF, FF, FF, 31, C0, C9, +ELF-i386: C3 ] +ELF-i386: alignment: 2^4 +ELF-i386: section-choice: custom-required +ELF-i386: section-name: .text.startup +ELF-i386: references: +ELF-i386: - kind: R_386_32 +ELF-i386: offset: 12 +ELF-i386: target: [[STRNAMEA]] +ELF-i386: - kind: R_386_PC32 +ELF-i386: offset: 17 +ELF-i386: target: puts +ELF-i386: addend: 252 +ELF-i386: undefined-atoms: +ELF-i386: - name: puts +ELF-i386: absolute-atoms: +ELF-i386: - name: test.c +ELF-i386: scope: static +ELF-i386: value: 0x0000000000000000 diff --git a/test/elf/responsefile.test b/test/elf/responsefile.test new file mode 100644 index 0000000000000..5957471bb6612 --- /dev/null +++ b/test/elf/responsefile.test @@ -0,0 +1,6 @@ +# RUN: not lld -flavor gnu --abc @%p/Inputs/responsefile --baz >& %t.log +# RUN: FileCheck %s < %t.log + +CHECK: warning: ignoring unknown argument: --abc +CHECK: warning: ignoring unknown argument: --inresponsefile +CHECK: warning: ignoring unknown argument: --baz diff --git a/test/elf/rodata.test b/test/elf/rodata.test new file mode 100644 index 0000000000000..dfe6985c07332 --- /dev/null +++ b/test/elf/rodata.test @@ -0,0 +1,5 @@ +RUN: lld -flavor gnu -target x86_64-linux -o %t %p/Inputs/constdata.x86-64 \ +RUN: -static -e _start +RUN: llvm-objdump -s %t | FileCheck %s + +CHECK: Hellooooooooo diff --git a/test/elf/rosegment.test b/test/elf/rosegment.test new file mode 100644 index 0000000000000..32638d8fce6fd --- /dev/null +++ b/test/elf/rosegment.test @@ -0,0 +1,26 @@ +# Tests that the option --rosegment produces an output file with a separate +# segment created for read only data. +RUN: lld -flavor gnu -target x86_64 %p/Inputs/rodata.o -o %t1.elf \ +RUN: --noinhibit-exec +RUN: lld -flavor gnu -target x86_64 %p/Inputs/rodata.o --rosegment -o %t2.elf \ +RUN: --noinhibit-exec +RUN: llvm-readobj -program-headers %t1.elf | FileCheck %s -check-prefix=NORO-SEGMENT +RUN: llvm-readobj -program-headers %t2.elf | FileCheck %s -check-prefix=RO-SEGMENT + +#NORO-SEGMENT: Type: PT_PHDR +#NORO-SEGMENT: Type: PT_INTERP +#NORO-SEGMENT: Type: PT_LOAD +#NORO-SEGMENT: Type: PT_LOAD +#NORO-SEGMENT: Type: PT_DYNAMIC +#NORO-SEGMENT: Type: PT_GNU_EH_FRAME + +#RO-SEGMENT: Type: PT_PHDR +#RO-SEGMENT: Type: PT_INTERP +#RO-SEGMENT: Type: PT_LOAD +#RO-SEGMENT: Type: PT_LOAD +#RO-SEGMENT: Flags [ +#RO-SEGMENT: PF_R (0x4) +#RO-SEGMENT: ] +#RO-SEGMENT: Type: PT_LOAD +#RO-SEGMENT: Type: PT_DYNAMIC +#RO-SEGMENT: Type: PT_GNU_EH_FRAME diff --git a/test/elf/sectionGroups/sectiongroup-new-members.test b/test/elf/sectionGroups/sectiongroup-new-members.test new file mode 100644 index 0000000000000..d270c5fec94a2 --- /dev/null +++ b/test/elf/sectionGroups/sectiongroup-new-members.test @@ -0,0 +1,153 @@ +# Checks that the linker picks the first group in the output file when the file +# have some members dont appear in the first group. +# 1a.s +# ------ +# +# .section .text,"axG",%progbits,foo_group,comdat +# .weak foo +#foo: +# .word 0 +# 1b.s +# ----- +# .section .text,"axG",%progbits,foo_group,comdat +# .global foo +# .global bar +#foo: +# .word 0 +#bar: +#RUN: yaml2obj -format=elf -docnum 1 %s -o %t.group1a.o +#RUN: yaml2obj -format=elf -docnum 2 %s -o %t.group1b.o +#RUN: lld -flavor gnu -target x86_64 %t.group1a.o %t.group1b.o \ +#RUN: --noinhibit-exec --output-filetype=yaml -o %t2.out.yaml +#RUN: lld -flavor gnu -target x86_64 %t.group1a.o %t.group1b.o \ +#RUN: --noinhibit-exec -o %t2.out +#RUN: FileCheck %s -check-prefix=CHECKGROUP < %t2.out.yaml +#RUN: llvm-readobj -sections %t2.out | FileCheck %s -check-prefix=CHECKGROUPSECTIONS +#RUN: llvm-readobj -symbols %t2.out | FileCheck %s -check-prefix=CHECKSYMBOLS +#CHECKGROUP: - name: foo +#CHECKGROUP: scope: global +#CHECKGROUP: merge: as-weak +#CHECKGROUP: section-name: .text +#CHECKGROUP: - name: foo_group +#CHECKGROUP: scope: global +#CHECKGROUP: type: group-comdat +#CHECKGROUP: section-choice: custom-required +#CHECKGROUP: section-name: .group +#CHECKGROUP: permissions: --- +#CHECKGROUP: references: +#CHECKGROUP: - kind: group-child +#CHECKGROUP: offset: 0 +#CHECKGROUP: target: foo +#CHECKGROUPSECTIONS: Section { +#CHECKGROUPSECTIONS: Name: .text +#CHECKGROUPSECTIONS: Type: SHT_PROGBITS +#CHECKGROUPSECTIONS: Flags [ (0x6) +#CHECKGROUPSECTIONS: SHF_ALLOC (0x2) +#CHECKGROUPSECTIONS: SHF_EXECINSTR (0x4) +#CHECKGROUPSECTIONS: ] +#CHECKGROUPSECTIONS: Size: 2 +#CHECKGROUPSECTIONS: AddressAlignment: 1 +#CHECKGROUPSECTIONS: } +#CHECKSYMBOLS: Name: foo +#CHECKSYMBOLS: Type: Function +#CHECKSYMBOLS: Section: .text +#CHECKSYMBOLS-NOT: Name: bar +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .group + Type: SHT_GROUP + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: foo_group + Members: + - SectionOrType: GRP_COMDAT + - SectionOrType: .text + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR, SHF_GROUP ] + AddressAlign: 0x0000000000000001 + Content: '0000' +Symbols: + Local: + - Name: foo_group + Section: .group + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + Weak: + - Name: foo + Section: .text +... +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .group + Type: SHT_GROUP + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: foo_group + Members: + - SectionOrType: GRP_COMDAT + - SectionOrType: .text + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR, SHF_GROUP ] + AddressAlign: 0x0000000000000001 + Content: '0000' +Symbols: + Local: + - Name: foo_group + Section: .group + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + Global: + - Name: bar + Section: .text + Value: 0x0000000000000002 + - Name: foo + Section: .text +... diff --git a/test/elf/sectionGroups/sectiongroup-simple.test b/test/elf/sectionGroups/sectiongroup-simple.test new file mode 100644 index 0000000000000..25be6033b0ef3 --- /dev/null +++ b/test/elf/sectionGroups/sectiongroup-simple.test @@ -0,0 +1,146 @@ +# Checks that the linker picks the first group in the output file when the file +# have some members dont appear in the first group. +# 1a.s +# ------ +# .section .text,"axG",%progbits,foo_group,comdat +# .weak foo +#foo: +# .word 0 +# 1b.s +# ----- +# .section .text,"axG",%progbits,foo_group,comdat +# .global bar +#bar: +#RUN: yaml2obj -format=elf -docnum 1 %s -o %t.group1a.o +#RUN: yaml2obj -format=elf -docnum 2 %s -o %t.group1b.o +#RUN: lld -flavor gnu -target x86_64 %t.group1a.o %t.group1b.o \ +#RUN: --noinhibit-exec --output-filetype=yaml -o %t2.out.yaml +#RUN: lld -flavor gnu -target x86_64 %t.group1a.o %t.group1b.o \ +#RUN: --noinhibit-exec -o %t2.out +#RUN: FileCheck %s -check-prefix=CHECKGROUP < %t2.out.yaml +#RUN: llvm-readobj -sections %t2.out | FileCheck %s -check-prefix=CHECKGROUPSECTIONS +#RUN: llvm-readobj -symbols %t2.out | FileCheck %s -check-prefix=CHECKSYMBOLS +#CHECKGROUP: - name: foo +#CHECKGROUP: scope: global +#CHECKGROUP: merge: as-weak +#CHECKGROUP: section-name: .text +#CHECKGROUP: - name: foo_group +#CHECKGROUP: scope: global +#CHECKGROUP: type: group-comdat +#CHECKGROUP: section-choice: custom-required +#CHECKGROUP: section-name: .group +#CHECKGROUP: permissions: --- +#CHECKGROUP: references: +#CHECKGROUP: - kind: group-child +#CHECKGROUP: offset: 0 +#CHECKGROUP: target: foo +#CHECKGROUPSECTIONS: Section { +#CHECKGROUPSECTIONS: Name: .text +#CHECKGROUPSECTIONS: Type: SHT_PROGBITS +#CHECKGROUPSECTIONS: Flags [ (0x6) +#CHECKGROUPSECTIONS: SHF_ALLOC (0x2) +#CHECKGROUPSECTIONS: SHF_EXECINSTR (0x4) +#CHECKGROUPSECTIONS: ] +#CHECKGROUPSECTIONS: Size: 2 +#CHECKGROUPSECTIONS: AddressAlignment: 1 +#CHECKGROUPSECTIONS: } +#CHECKSYMBOLS: Name: foo +#CHECKSYMBOLS: Type: Function +#CHECKSYMBOLS: Section: .text + +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .group + Type: SHT_GROUP + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: foo_group + Members: + - SectionOrType: GRP_COMDAT + - SectionOrType: .text + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR, SHF_GROUP ] + AddressAlign: 0x0000000000000001 + Content: '0000' +Symbols: + Local: + - Name: foo_group + Section: .group + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + Weak: + - Name: foo + Section: .text +... +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .group + Type: SHT_GROUP + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: foo_group + Members: + - SectionOrType: GRP_COMDAT + - SectionOrType: .text + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR, SHF_GROUP ] + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Local: + - Name: foo_group + Section: .group + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + Global: + - Name: bar + Section: .text +... diff --git a/test/elf/sectionGroups/sectiongroup-undef-member-other.test b/test/elf/sectionGroups/sectiongroup-undef-member-other.test new file mode 100644 index 0000000000000..78a5f276a4cac --- /dev/null +++ b/test/elf/sectionGroups/sectiongroup-undef-member-other.test @@ -0,0 +1,158 @@ +# Tests that linker throws an error for an undefined symbol in the section +# group, which is the same as the signature in the next input file. +# comdat1.s +# ------------ +# .section .foo,"axG",@progbits,g1,comdat +# .word g1 +#comdat2.s +#----------- +# .global g1 +# .section .bar,"axG",@progbits,g1,comdat +#g2: +# nop +# .section .car,"axG",@progbits,g1,comdat +#g3: +# nop +# +#RUN: yaml2obj -format=elf -docnum 1 %s -o %t.group1a.o +#RUN: yaml2obj -format=elf -docnum 2 %s -o %t.group1b.o +#RUN: lld -flavor gnu -target x86_64 %t.group1a.o %t.group1b.o \ +#RUN: --noinhibit-exec -o %t2.out 2>&1 | FileCheck %s +#CHECK: Undefined symbol: {{.*}} g1 +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .group + Type: SHT_GROUP + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: g1 + Members: + - SectionOrType: GRP_COMDAT + - SectionOrType: .foo + - SectionOrType: .rela.foo + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .foo + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR, SHF_GROUP ] + AddressAlign: 0x0000000000000001 + Content: '0000' + - Name: .rela.foo + Type: SHT_RELA + Flags: [ SHF_GROUP ] + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .foo + Relocations: + - Offset: 0x0000000000000000 + Symbol: g1 + Type: R_X86_64_16 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .foo + Type: STT_SECTION + Section: .foo + - Name: .group + Type: STT_SECTION + Section: .group + Global: + - Name: g1 +... +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .group + Type: SHT_GROUP + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: g1 + Members: + - SectionOrType: GRP_COMDAT + - SectionOrType: .bar + - SectionOrType: .car + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bar + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR, SHF_GROUP ] + AddressAlign: 0x0000000000000001 + Content: '90' + - Name: .car + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR, SHF_GROUP ] + AddressAlign: 0x0000000000000001 + Content: '90' +Symbols: + Local: + - Name: g2 + Section: .bar + - Name: g3 + Section: .car + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .bar + Type: STT_SECTION + Section: .bar + - Name: .car + Type: STT_SECTION + Section: .car + - Name: .group + Type: STT_SECTION + Section: .group + Global: + - Name: g1 + Section: .group +... diff --git a/test/elf/sectionGroups/sectiongroup-undef-member.test b/test/elf/sectionGroups/sectiongroup-undef-member.test new file mode 100644 index 0000000000000..2f6804d254d80 --- /dev/null +++ b/test/elf/sectionGroups/sectiongroup-undef-member.test @@ -0,0 +1,144 @@ +# Tests that linker throws an error for an undefined symbol in the section +# group. +# +#comdata.s +#------------ +# .section .foo,"axG",@progbits,g1,comdat +# .word g1 +# +#comdatb.s +#------------ +# .global g1 +# .section .foo,"axG",@progbits,g1,comdat +#g1: +# nop +# +#RUN: yaml2obj -format=elf -docnum 1 %s -o %t.group1a.o +#RUN: yaml2obj -format=elf -docnum 2 %s -o %t.group1b.o +#RUN: lld -flavor gnu -target x86_64 %t.group1a.o %t.group1b.o \ +#RUN: --noinhibit-exec -o %t2.out 2>&1 | FileCheck %s +#CHECK: Undefined symbol: {{.*}} g1 +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .group + Type: SHT_GROUP + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: g1 + Members: + - SectionOrType: GRP_COMDAT + - SectionOrType: .foo + - SectionOrType: .rela.foo + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .foo + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR, SHF_GROUP ] + AddressAlign: 0x0000000000000001 + Content: '0000' + - Name: .rela.foo + Type: SHT_RELA + Flags: [ SHF_GROUP ] + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .foo + Relocations: + - Offset: 0x0000000000000000 + Symbol: g1 + Type: R_X86_64_16 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .foo + Type: STT_SECTION + Section: .foo + - Name: .group + Type: STT_SECTION + Section: .group + Global: + - Name: g1 +... +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .group + Type: SHT_GROUP + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: g1 + Members: + - SectionOrType: GRP_COMDAT + - SectionOrType: .foo + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .foo + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR, SHF_GROUP ] + AddressAlign: 0x0000000000000001 + Content: '90' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .foo + Type: STT_SECTION + Section: .foo + - Name: .group + Type: STT_SECTION + Section: .group + Global: + - Name: g1 + Section: .foo +... diff --git a/test/elf/sectionGroups/sectiongroup-with-globalsymbols.test b/test/elf/sectionGroups/sectiongroup-with-globalsymbols.test new file mode 100644 index 0000000000000..0a28e3c98907f --- /dev/null +++ b/test/elf/sectionGroups/sectiongroup-with-globalsymbols.test @@ -0,0 +1,253 @@ +# This tests that comdat weak symbol can be overridden by a global symbol. +# comdat1.s +#------------------------ +# .weak g1 +# .section .foo,"axG",@progbits,g1,comdat +#g1: +# nop +# .global g2 +#g2: +# nop +#! +# +#comdat2.s << \! +#----------------- +# .global g1 +# .section .foo,"axG",@progbits,g1,comdat +#g1: +# nop +# .global g2 +#g2: +# nop +# +#cat > g1.c << \! +#int g1() { +# return 0; +#} +# +#RUN: yaml2obj -format=elf -docnum 1 %s -o %t.comdat1.o +#RUN: yaml2obj -format=elf -docnum 2 %s -o %t.comdat2.o +#RUN: yaml2obj -format=elf -docnum 3 %s -o %t.g1.o +#RUN: lld -flavor gnu -target x86_64 %t.comdat1.o %t.comdat2.o \ +#RUN: %t.g1.o --noinhibit-exec --output-filetype=yaml -o %t2.out.yaml +#RUN: lld -flavor gnu -target x86_64 %t.comdat1.o %t.comdat2.o \ +#RUN: %t.g1.o --noinhibit-exec -o %t2.out +#RUN: FileCheck %s -check-prefix=CHECKGROUP < %t2.out.yaml +#RUN: llvm-readobj -sections %t2.out | FileCheck %s -check-prefix=CHECKGROUPSECTIONS +#RUN: llvm-readobj -symbols %t2.out | FileCheck %s -check-prefix=CHECKSYMBOLS +#CHECKGROUP: - name: g2 +#CHECKGROUP: content: [ 90 ] +#CHECKGROUP: section-choice: custom-required +#CHECKGROUP: section-name: .foo +#CHECKGROUP: - name: g1 +#CHECKGROUP: scope: global +#CHECKGROUP: content: [ 55, 48, 89, E5, 31, C0, 5D, C3 ] +#CHECKGROUP: alignment: 2^4 +#CHECKGROUP: section-name: .text +#CHECKGROUPSECTIONS: Name: .text +#CHECKGROUPSECTIONS: Type: SHT_PROGBITS +#CHECKGROUPSECTIONS: Flags [ +#CHECKGROUPSECTIONS: SHF_ALLOC +#CHECKGROUPSECTIONS: SHF_EXECINSTR +#CHECKGROUPSECTIONS: ] +#CHECKGROUPSECTIONS: Size: 8 +#CHECKGROUPSECTIONS: Name: .foo +#CHECKGROUPSECTIONS: Type: SHT_PROGBITS +#CHECKGROUPSECTIONS: Flags [ +#CHECKGROUPSECTIONS: SHF_ALLOC +#CHECKGROUPSECTIONS: SHF_EXECINSTR +#CHECKGROUPSECTIONS: ] +#CHECKGROUPSECTIONS: Size: 2 +#CHECKSYMBOLS: Name: g2 +#CHECKSYMBOLS: Section: .foo +#CHECKSYMBOLS: Name: g1 +#CHECKSYMBOLS: Section: .text +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .group + Type: SHT_GROUP + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: g1 + Members: + - SectionOrType: GRP_COMDAT + - SectionOrType: .foo + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .foo + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR, SHF_GROUP ] + AddressAlign: 0x0000000000000001 + Content: '9090' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .foo + Type: STT_SECTION + Section: .foo + Global: + - Name: g2 + Section: .foo + Value: 0x0000000000000001 + Weak: + - Name: g1 + Section: .foo +... +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .group + Type: SHT_GROUP + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: g1 + Members: + - SectionOrType: GRP_COMDAT + - SectionOrType: .foo + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .foo + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR, SHF_GROUP ] + AddressAlign: 0x0000000000000001 + Content: '9090' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .foo + Type: STT_SECTION + Section: .foo + Global: + - Name: g1 + Section: .foo + - Name: g2 + Section: .foo + Value: 0x0000000000000001 +... +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E531C05DC3 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 00636C616E672076657273696F6E20332E372E3020287472756E6B203232393535372920286C6C766D2F7472756E6B203232393536332900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: 1400000000000000037A5200017810011B0C0708900100001C0000001C000000000000000800000000410E108602430D0600000000000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .eh_frame + Relocations: + - Offset: 0x0000000000000020 + Symbol: .text + Type: R_X86_64_PC32 +Symbols: + Local: + - Name: g1.c + Type: STT_FILE + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .comment + Type: STT_SECTION + Section: .comment + - Name: .note.GNU-stack + Type: STT_SECTION + Section: .note.GNU-stack + - Name: .eh_frame + Type: STT_SECTION + Section: .eh_frame + Global: + - Name: g1 + Type: STT_FUNC + Section: .text + Size: 0x0000000000000008 +... diff --git a/test/elf/sectionGroups/sectiongroup-with-undef-external-reference.test b/test/elf/sectionGroups/sectiongroup-with-undef-external-reference.test new file mode 100644 index 0000000000000..a90034ae6d214 --- /dev/null +++ b/test/elf/sectionGroups/sectiongroup-with-undef-external-reference.test @@ -0,0 +1,239 @@ +# This tests that comdat undef symbol is overridden by a global symbol. +# comdat1.s +#------------------------ +# .global g1 +# .section .foo,"axG",@progbits,g1,comdat +#g1: +# .word 5 +# +#comdat2.s << \! +#----------------- +# .global g1 +# .section .foo,"axG",@progbits,g1,comdat +#g1: +# nop +# +#g1.c +#----------- +#extern int g1; +#int fn() { return g1;} +# +#RUN: yaml2obj -format=elf -docnum 1 %s -o %t.comdat1.o +#RUN: yaml2obj -format=elf -docnum 2 %s -o %t.comdat2.o +#RUN: yaml2obj -format=elf -docnum 3 %s -o %t.g1.o +#RUN: lld -flavor gnu -target x86_64 %t.comdat1.o %t.comdat2.o \ +#RUN: %t.g1.o --noinhibit-exec --output-filetype=yaml -o %t2.out.yaml +#RUN: lld -flavor gnu -target x86_64 %t.comdat1.o %t.comdat2.o \ +#RUN: %t.g1.o --noinhibit-exec -o %t2.out +#RUN: FileCheck %s -check-prefix=CHECKGROUP < %t2.out.yaml +#RUN: llvm-readobj -sections %t2.out | FileCheck %s -check-prefix=CHECKGROUPSECTIONS +#RUN: llvm-readobj -symbols %t2.out | FileCheck %s -check-prefix=CHECKSYMBOLS +#CHECKGROUP: - name: g1 +#CHECKGROUP: scope: global +#CHECKGROUP: content: [ 05, 00 ] +#CHECKGROUP: section-name: .foo +#CHECKGROUPSECTIONS: Section { +#CHECKGROUPSECTIONS: Name: .foo +#CHECKGROUPSECTIONS: Type: SHT_PROGBITS +#CHECKGROUPSECTIONS: Flags [ +#CHECKGROUPSECTIONS: SHF_ALLOC +#CHECKGROUPSECTIONS: SHF_EXECINSTR +#CHECKGROUPSECTIONS: ] +#CHECKGROUPSECTIONS: Size: 2 +#CHECKGROUPSECTIONS: } +#CHECKSYMBOLS: Name: g1 +#CHECKSYMBOLS: Section: .foo +#CHECKSYMBOLS: Name: fn +#CHECKSYMBOLS: Section: .text +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .group + Type: SHT_GROUP + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: g1 + Members: + - SectionOrType: GRP_COMDAT + - SectionOrType: .foo + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .foo + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR, SHF_GROUP ] + AddressAlign: 0x0000000000000001 + Content: '0500' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .foo + Type: STT_SECTION + Section: .foo + Global: + - Name: g1 + Section: .foo +... +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .group + Type: SHT_GROUP + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: g1 + Members: + - SectionOrType: GRP_COMDAT + - SectionOrType: .foo + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .foo + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR, SHF_GROUP ] + AddressAlign: 0x0000000000000001 + Content: '90' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .foo + Type: STT_SECTION + Section: .foo + Global: + - Name: g1 + Section: .foo +... +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E58B0425000000005DC3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x0000000000000007 + Symbol: g1 + Type: R_X86_64_32S + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 00636C616E672076657273696F6E20332E372E3020287472756E6B203232393535372920286C6C766D2F7472756E6B203232393536332900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: 1400000000000000037A5200017810011B0C0708900100001C0000001C000000000000000D00000000410E108602430D0600000000000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .eh_frame + Relocations: + - Offset: 0x0000000000000020 + Symbol: .text + Type: R_X86_64_PC32 +Symbols: + Local: + - Name: global-g1.c + Type: STT_FILE + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .comment + Type: STT_SECTION + Section: .comment + - Name: .note.GNU-stack + Type: STT_SECTION + Section: .note.GNU-stack + - Name: .eh_frame + Type: STT_SECTION + Section: .eh_frame + Global: + - Name: fn + Type: STT_FUNC + Section: .text + Size: 0x000000000000000D + - Name: g1 +... diff --git a/test/elf/sectionGroups/sectiongroup-with-undef-signature.test b/test/elf/sectionGroups/sectiongroup-with-undef-signature.test new file mode 100644 index 0000000000000..11cb5de40a631 --- /dev/null +++ b/test/elf/sectionGroups/sectiongroup-with-undef-signature.test @@ -0,0 +1,222 @@ +# This tests that comdat undef symbol is overridden by a global symbol. +# comdat1.s +#------------------------ +# .section .foo,"axG",@progbits,g1,comdat +# word g1 +# +#comdat2.s << \! +#----------------- +# .global g1 +# .section .foo,"axG",@progbits,g1,comdat +#g1: +# nop +# +#global-g1.c +#----------- +#int g1=10; +# +#RUN: yaml2obj -format=elf -docnum 1 %s -o %t.comdat1.o +#RUN: yaml2obj -format=elf -docnum 2 %s -o %t.comdat2.o +#RUN: yaml2obj -format=elf -docnum 3 %s -o %t.g1.o +#RUN: lld -flavor gnu -target x86_64 %t.comdat1.o %t.comdat2.o \ +#RUN: %t.g1.o --noinhibit-exec --output-filetype=yaml -o %t2.out.yaml +#RUN: lld -flavor gnu -target x86_64 %t.comdat1.o %t.comdat2.o \ +#RUN: %t.g1.o --noinhibit-exec -o %t2.out +#RUN: FileCheck %s -check-prefix=CHECKGROUP < %t2.out.yaml +#RUN: llvm-readobj -sections %t2.out | FileCheck %s -check-prefix=CHECKGROUPSECTIONS +#RUN: llvm-readobj -symbols %t2.out | FileCheck %s -check-prefix=CHECKSYMBOLS +#CHECKGROUP: - name: g1 +#CHECKGROUP: scope: global +#CHECKGROUP: content: [ 0A, 00, 00, 00 ] +#CHECKGROUP: section-name: .data +#CHECKGROUPSECTIONS: Name: .foo +#CHECKGROUPSECTIONS: Type: SHT_PROGBITS +#CHECKGROUPSECTIONS: Flags [ +#CHECKGROUPSECTIONS: SHF_ALLOC +#CHECKGROUPSECTIONS: SHF_EXECINSTR +#CHECKGROUPSECTIONS: ] +#CHECKGROUPSECTIONS: Size: 2 +#CHECKGROUPSECTIONS: Name: .data +#CHECKGROUPSECTIONS: Type: SHT_PROGBITS +#CHECKGROUPSECTIONS: Flags [ +#CHECKGROUPSECTIONS: SHF_ALLOC +#CHECKGROUPSECTIONS: SHF_WRITE +#CHECKGROUPSECTIONS: ] +#CHECKGROUPSECTIONS: Size: 4 +#CHECKSYMBOLS: Name: g1 +#CHECKSYMBOLS: Section: .data +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .group + Type: SHT_GROUP + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: g1 + Members: + - SectionOrType: GRP_COMDAT + - SectionOrType: .foo + - SectionOrType: .rela.foo + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .foo + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR, SHF_GROUP ] + AddressAlign: 0x0000000000000001 + Content: '0000' + - Name: .rela.foo + Type: SHT_RELA + Flags: [ SHF_GROUP ] + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .foo + Relocations: + - Offset: 0x0000000000000000 + Symbol: g1 + Type: R_X86_64_16 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .foo + Type: STT_SECTION + Section: .foo + Global: + - Name: g1 +... +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .group + Type: SHT_GROUP + Link: .symtab + AddressAlign: 0x0000000000000004 + Info: g1 + Members: + - SectionOrType: GRP_COMDAT + - SectionOrType: .foo + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .foo + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR, SHF_GROUP ] + AddressAlign: 0x0000000000000001 + Content: '90' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .foo + Type: STT_SECTION + Section: .foo + Global: + - Name: g1 + Section: .foo +... +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: 0A000000 + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 00636C616E672076657273696F6E20332E372E3020287472756E6B203232393535372920286C6C766D2F7472756E6B203232393536332900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' +Symbols: + Local: + - Name: global-g1.c + Type: STT_FILE + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .comment + Type: STT_SECTION + Section: .comment + - Name: .note.GNU-stack + Type: STT_SECTION + Section: .note.GNU-stack + Global: + - Name: g1 + Type: STT_OBJECT + Section: .data + Size: 0x0000000000000004 +... diff --git a/test/elf/sections.test b/test/elf/sections.test new file mode 100644 index 0000000000000..8839aa6d4e8ab --- /dev/null +++ b/test/elf/sections.test @@ -0,0 +1,142 @@ +# This test checks if sections are created properly in the output that appear in +# the input +RUN: lld -flavor gnu -target i386 -o %t1 %p/Inputs/section-test.i386 \ +RUN: -static -e baz +RUN: llvm-objdump -section-headers %t1 | FileCheck -check-prefix=OBJDUMP %s +RUN: llvm-readobj -h -s -t %t1 | FileCheck -check-prefix=READOBJ %s + +OBJDUMP: 0 00000000 0000000000000000 +OBJDUMP: 1 .text 0000000a 0000000000000074 TEXT DATA +OBJDUMP: 2 .data 00000004 0000000000001000 DATA +OBJDUMP: 3 .special 00000004 0000000000001004 DATA +OBJDUMP: 4 .anotherspecial 00000004 0000000000001008 DATA +OBJDUMP: 5 .bss 00000004 000000000000100c BSS +OBJDUMP: 6 .shstrtab {{[0-9a-f]+}} 0000000000000000 +OBJDUMP: 7 .symtab {{[0-9a-f]+}} 0000000000000000 +OBJDUMP: 8 .strtab {{[0-9a-f]+}} 0000000000000000 + +READOBJ: Format: ELF32-i386 +READOBJ: Arch: i386 +READOBJ: AddressSize: 32bit +READOBJ: ElfHeader { +READOBJ: Ident { +READOBJ: DataEncoding: LittleEndian (0x1) +READOBJ: } +READOBJ: Machine: EM_386 +READOBJ: } + +READOBJ: Sections [ +READOBJ: Section { +READOBJ: Index: 0 +READOBJ: Name: (0) +READOBJ: Type: SHT_NULL +READOBJ: Flags [ (0x0) +READOBJ: ] +READOBJ: Address: 0x0 +READOBJ: Size: 0 +READOBJ: } +READOBJ: Section { +READOBJ: Index: 1 +READOBJ: Name: .text +READOBJ: Type: SHT_PROGBITS +READOBJ: Flags [ (0x6) +READOBJ: SHF_ALLOC +READOBJ: SHF_EXECINSTR +READOBJ: ] +READOBJ: Address: 0x74 +READOBJ: Size: 10 +READOBJ: AddressAlignment: 4 +READOBJ: } +READOBJ: Section { +READOBJ: Index: 2 +READOBJ: Name: .data +READOBJ: Type: SHT_PROGBITS +READOBJ: Flags [ (0x3) +READOBJ: SHF_ALLOC +READOBJ: SHF_WRITE +READOBJ: ] +READOBJ: Address: 0x1000 +READOBJ: Size: 4 +READOBJ: AddressAlignment: 4 +READOBJ: } +READOBJ: Section { +READOBJ: Index: 3 +READOBJ: Name: .special +READOBJ: Type: SHT_PROGBITS +READOBJ: Flags [ (0x3) +READOBJ: SHF_ALLOC +READOBJ: SHF_WRITE +READOBJ: ] +READOBJ: Address: 0x1004 +READOBJ: Size: 4 +READOBJ: } +READOBJ: Section { +READOBJ: Index: 4 +READOBJ: Name: .anotherspecial +READOBJ: Type: SHT_PROGBITS +READOBJ: Flags [ (0x3) +READOBJ: SHF_ALLOC +READOBJ: SHF_WRITE +READOBJ: ] +READOBJ: Address: 0x1008 +READOBJ: Size: 4 +READOBJ: } +READOBJ: Section { +READOBJ: Index: 5 +READOBJ: Name: .bss +READOBJ: Type: SHT_NOBITS +READOBJ: Flags [ (0x3) +READOBJ: SHF_ALLOC +READOBJ: SHF_WRITE +READOBJ: ] +READOBJ: Address: 0x100C +READOBJ: Size: 4 +READOBJ: } +READOBJ: Section { +READOBJ: Index: 6 +READOBJ: Name: .shstrtab +READOBJ: Type: SHT_STRTAB +READOBJ: Flags [ (0x0) +READOBJ: ] +READOBJ: Address: 0x0 +READOBJ: Link: 0 +READOBJ: AddressAlignment: 1 +READOBJ: } +READOBJ: Section { +READOBJ: Index: 7 +READOBJ: Name: .symtab +READOBJ: Type: SHT_SYMTAB +READOBJ: Flags [ (0x0) +READOBJ: ] +READOBJ: Address: 0x0 +READOBJ: Link: 8 +READOBJ: AddressAlignment: 4 +READOBJ: EntrySize: 16 +READOBJ: } +READOBJ: Section { +READOBJ: Index: 8 +READOBJ: Name: .strtab +READOBJ: Type: SHT_STRTAB +READOBJ: Flags [ (0x0) +READOBJ: ] +READOBJ: Address: 0x0 +READOBJ: } +READOBJ: ] + +READOBJ: Symbols [ +READOBJ: Symbol { +READOBJ: Name: baz +READOBJ: Value: 0x74 +READOBJ: Size: 10 +READOBJ: Binding: Global +READOBJ: Type: Function +READOBJ: Section: .text +READOBJ: } +READOBJ: Symbol { +READOBJ: Name: y +READOBJ: Value: 0x1004 +READOBJ: Size: 4 +READOBJ: Binding: Global +READOBJ: Type: Object +READOBJ: Section: .special +READOBJ: } diff --git a/test/elf/sh_addralign.test b/test/elf/sh_addralign.test new file mode 100644 index 0000000000000..9c7a050f565c6 --- /dev/null +++ b/test/elf/sh_addralign.test @@ -0,0 +1,38 @@ +# Check handling of section alignment. +# RUN: yaml2obj -format=elf %s > %t-obj +# RUN: lld -flavor gnu -target arm64 -o %t-exe %t-obj +# RUN: llvm-objdump -h %t-exe | FileCheck %s + +# CHECK: 8 .data 00000000 0000000000402000 DATA + +!ELF +FileHeader: !FileHeader + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_AARCH64 + +Sections: +- Name: .text + Type: SHT_PROGBITS + Content: '' + AddressAlign: 0 + Flags: [SHF_ALLOC, SHF_EXECINSTR] +- Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 1 + Content: '' + Size: 0x1000 +- Name: .data + Type: SHT_PROGBITS + Content: '' + AddressAlign: 4096 + Flags: [SHF_ALLOC, SHF_WRITE] + +Symbols: + Global: + - Name: _start + Section: .text + Value: 0x0 + Size: 4 diff --git a/test/elf/soname.test b/test/elf/soname.test new file mode 100644 index 0000000000000..79c089ec607c4 --- /dev/null +++ b/test/elf/soname.test @@ -0,0 +1,6 @@ +RUN: lld -flavor gnu -shared -target i386 -e main %p/Inputs/writersyms.o \ +RUN: -o %t -soname libtest.so +RUN: llvm-readobj -dynamic-table %t | FileCheck %s + +CHECK: LoadName: libtest.so +CHECK: 0x0000000E SONAME LibrarySoname (libtest.so) diff --git a/test/elf/strip-all.test b/test/elf/strip-all.test new file mode 100644 index 0000000000000..44a41c2a0d9fd --- /dev/null +++ b/test/elf/strip-all.test @@ -0,0 +1,107 @@ +# Tests the --strip-all (-s) flag. We expect the symbol table to not contain +# any symbol in the output file. +# +# The following code was used to generate the object. +# $ clang -c blah.c -o blah +# +# void +# callMeMaybe(int *v) +# { +# *v += 1; +# } +# +# int +# main(void) +# { +# +# int blah = 42; +# callMeMaybe(&blah); +# } + +#RUN: yaml2obj -format=elf %s -o=%t.o +#RUN: lld -flavor gnu -target x86_64 %t.o -e=main --strip-all -o %t1 +#RUN: llvm-readobj -dt %t1 | FileCheck -check-prefix CHECKSYMS %s + +#CHECKSYMS: @ + +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_FREEBSD + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E548897DF8488B7DF88B07050100000089075DC3660F1F840000000000554889E54883EC10488D7DFCC745FC2A000000E8C8FFFFFFB8000000004883C4105DC3 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 004672656542534420636C616E672076657273696F6E20332E342E312028746167732F52454C454153455F33342F646F74312D66696E616C203230383033322920323031343035313200 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: 1400000000000000017A5200017810011B0C070890010000180000001C000000000000001700000000410E108602430D060000001800000038000000000000002300000000410E108602430D06000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .eh_frame + Relocations: + - Offset: 0x0000000000000020 + Symbol: .text + Type: R_X86_64_PC32 + - Offset: 0x000000000000003C + Symbol: .text + Type: R_X86_64_PC32 + Addend: 32 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .comment + Type: STT_SECTION + Section: .comment + - Name: .note.GNU-stack + Type: STT_SECTION + Section: .note.GNU-stack + - Name: .eh_frame + Type: STT_SECTION + Section: .eh_frame + Global: + - Name: callMeMaybe + Type: STT_FUNC + Section: .text + Size: 0x0000000000000017 + - Name: main + Type: STT_FUNC + Section: .text + Value: 0x0000000000000020 + Size: 0x0000000000000023 +... diff --git a/test/elf/stripped-empty.test b/test/elf/stripped-empty.test new file mode 100644 index 0000000000000..0403808db2ebf --- /dev/null +++ b/test/elf/stripped-empty.test @@ -0,0 +1,4 @@ +RUN: lld -flavor gnu -shared -o test.so \ +RUN: -target x86_64 %p/Inputs/stripped-empty.x86_64 + +test that we handle files without a symbol table. diff --git a/test/elf/symbols.test b/test/elf/symbols.test new file mode 100644 index 0000000000000..7f6bbdbc8c1d0 --- /dev/null +++ b/test/elf/symbols.test @@ -0,0 +1,33 @@ +# Tests the functionality of archive libraries reading +# and resolution +# Note: The binary files would not be required once we have support to generate +# binary archives from textual(yaml) input +# +# Tests generated using the source files below +# main file +# +#extern int __bss_start __attribute__ ((weak)); +#int a; +#int main() +#{ +# return 0; +#} +# + +RUN: lld -flavor gnu -target i386 -e main %p/Inputs/writersyms.o -o %t1 +RUN: llvm-nm -n %t1 | FileCheck -check-prefix CHECKSYMS %s +RUN: lld -flavor gnu -shared -target i386 -e main %p/Inputs/writersyms.o -o %t1 +RUN: llvm-nm -n %t1 | FileCheck -check-prefix CHECKSHAREDSYMS %s + +CHECKSYMS: {{[0-9a-f]+}} a 1.c +CHECKSYMS: {{[0-9a-f]+}} T main +CHECKSYMS: {{[0-9a-f]+}} A __bss_start +CHECKSYMS: {{[0-9a-f]+}} B a +CHECKSYMS: {{[0-9a-f]+}} A __bss_end +CHECKSYMS: {{[0-9a-f]+}} A _end +CHECKSYMS: {{[0-9a-f]+}} A end + +CHECKSHAREDSYMS: {{[0-9a-f]+}} a 1.c +CHECKSHAREDSYMS: {{[0-9a-f]+}} T main +CHECKSHAREDSYMS: {{[0-9a-f]+}} B a +CHECKSHAREDSYMS: {{[0-9a-f]+}} A _end diff --git a/test/elf/tls.test b/test/elf/tls.test new file mode 100644 index 0000000000000..038889406b671 --- /dev/null +++ b/test/elf/tls.test @@ -0,0 +1,43 @@ +# REQUIRES: x86 + +# This tests verifies that TLS variables have correct offsets +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/tls.x86-64 -static \ +RUN: --output-filetype=yaml --noinhibit-exec | FileCheck %s -check-prefix=YAML + +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/tls.x86-64 -o %t \ +RUN: --noinhibit-exec -e main -static && llvm-objdump -d %t | FileCheck %s + +// Verify that the TLS accesses have the correct offsets. + +YAML: name: main +YAML: references: +YAML: kind: R_X86_64_TPOFF32 +YAML: offset: 9 +YAML: target: tls1 +YAML: kind: R_X86_64_TPOFF32 +YAML: offset: 17 +YAML: target: tls0 +YAML: kind: R_X86_64_TPOFF32 +YAML: offset: 25 +YAML: target: tls2 + +YAML: name: GOTTPOFF +YAML: kind: R_X86_64_PC32 +YAML: target: [[GOTNAME:[a-zA-Z0-9_]+]] + +YAML: type: got +YAML: references: +YAML: kind: R_X86_64_TPOFF64 +YAML: target: tls2 + +// main +CHECK: addl %fs:-4 +CHECK: addl %fs:-8 +CHECK: addl %fs:-12 + +// GOTTPOFF +CHECK: movq {{[0-9]+}}(%rip) + +// TLSLD +CHECK: movq %fs:0, %rax +CHECK: leaq -8(%rax), %rax diff --git a/test/elf/tlsAddr.test b/test/elf/tlsAddr.test new file mode 100644 index 0000000000000..6bc5e3e9bf789 --- /dev/null +++ b/test/elf/tlsAddr.test @@ -0,0 +1,7 @@ +# This tests verifies that TLS variables have correct offsets +# and that TBSS doesn't occupy memory +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/tlsAddr.x86-64 -static \ +RUN: -o %t --noinhibit-exec +RUN: llvm-objdump -section-headers %t | FileCheck -check-prefix=CHECKADDR %s + +CHECKADDR: {{[0-9]+}} .data 00000000 0000000000401008 DATA diff --git a/test/elf/undef-from-dso-to-main.test b/test/elf/undef-from-dso-to-main.test new file mode 100644 index 0000000000000..71d0b51499ddd --- /dev/null +++ b/test/elf/undef-from-dso-to-main.test @@ -0,0 +1,52 @@ +# Tests that a reference from a DSO to a regular object +# forces the final executable to export the symbol. + +#RUN: yaml2obj -format=elf %p/Inputs/undef2-so.o.yaml -o=%t.o.so +#RUN: lld -flavor gnu -target x86_64 -shared %t.o.so -o %T/libundef2.so +#RUN: yaml2obj -format=elf %s -o=%t.o +#RUN: lld -flavor gnu -target x86_64 %t.o -L%T -lundef2 -o %t1 +#RUN: llvm-readobj -dyn-symbols %t1 | FileCheck -check-prefix CHECKSYMS %s + +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000001 + Content: C3 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '002E7379' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + Global: + - Name: myexportedsymbol + Type: STT_OBJECT + Section: .bss + Size: 0x0000000000000004 + - Name: _start + Section: .text + Size: 0x0000000000000001 + +#CHECKSYMS: myexportedsymbol diff --git a/test/elf/undef-from-main-dso.test b/test/elf/undef-from-main-dso.test new file mode 100644 index 0000000000000..43faef0e74c42 --- /dev/null +++ b/test/elf/undef-from-main-dso.test @@ -0,0 +1,43 @@ +RUN: lld -flavor gnu -target x86_64 -e main -o %t -L%p/Inputs \ +RUN: %p/Inputs/undef.o -lundef +RUN: llvm-readobj -relocations -symbols -dyn-symbols %t | FileCheck %s + +RUN: lld -flavor gnu -target x86_64 -e main -o %t -L%p/Inputs \ +RUN: %p/Inputs/undef-pc32.o -lundef +RUN: llvm-readobj -relocations -symbols -dyn-symbols %t | FileCheck %s + +# DSO source code: +# int x[2] = { 1, 2 }; +# +# Main binary source code: +# +# extern int x[2]; +# +# int main(void) +# { +# x[0] = 2; +# } +# + +CHECK: Relocations [ +CHECK-NEXT: Section ({{[0-9]+}}) .rela.dyn { +CHECK-NEXT: 0x{{[1-9A-F][0-9A-F]*}} R_X86_64_COPY x 0x0 +CHECK-NEXT: } +CHECK-NEXT: ] + +CHECK: Name: x ({{[0-9]+}} +CHECK-NEXT: Value: 0x{{[1-9A-F][0-9A-F]*}} +CHECK-NEXT: Size: 8 +CHECK-NEXT: Binding: Global (0x1) +CHECK-NEXT: Type: Object (0x1) +CHECK-NEXT: Other: 0 +CHECK-NEXT: Section: .bss + +CHECK: Name: x@ ({{[0-9]+}} +CHECK-NEXT: Value: 0x{{[1-9A-F][0-9A-F]*}} +CHECK-NEXT: Size: 8 +CHECK-NEXT: Binding: Global (0x1) +CHECK-NEXT: Type: Object (0x1) +CHECK-NEXT: Other: 0 +CHECK-NEXT: Section: .bss + diff --git a/test/elf/weaksym.test b/test/elf/weaksym.test new file mode 100644 index 0000000000000..0e265028e5edb --- /dev/null +++ b/test/elf/weaksym.test @@ -0,0 +1,7 @@ +# Tests that a weak reference remains a weak reference, +# even if a shared library defines the symbol weak as well. + +RUN: lld -flavor gnu -target x86_64 -shared %p/Inputs/weaksym.o -L%p/Inputs -lweaksym -o %t1 +RUN: llvm-nm -n %t1 | FileCheck -check-prefix CHECKSYMS %s + +CHECKSYMS: w weaksym diff --git a/test/elf/wrap.test b/test/elf/wrap.test new file mode 100644 index 0000000000000..997439f8f5b4f --- /dev/null +++ b/test/elf/wrap.test @@ -0,0 +1,279 @@ +# This tests the functionality of using the --wrap option. +# The test case is extracted by compiling and linking the following code. +# +#cat > main.c << \! +#int main() { +# foo(); +# return 0; +#} +#! +# +#cat > wrapfoo.c << \! +#int __wrap_foo() { +# __real_foo(); +# return 0; +#} +#! +# +#cat > realfoo.c << \! +#int foo() { +# return 0; +#} +#! +# +#clang main.c wrapfoo.c realfoo.c -Xlinker --wrap -Xlinker foo +#RUN: yaml2obj -format=elf -docnum 1 %s -o %t.main.o +#RUN: yaml2obj -format=elf -docnum 2 %s -o %t.wrapfoo.o +#RUN: yaml2obj -format=elf -docnum 3 %s -o %t.realfoo.o +#RUN: lld -flavor gnu -target x86_64 %t.main.o %t.wrapfoo.o %t.realfoo.o \ +#RUN: --wrap foo --wrap foo --noinhibit-exec --output-filetype=yaml -o %t2.out +#RUN: lld -flavor gnu -target x86_64 %t.main.o %t.wrapfoo.o \ +#RUN: --wrap foo --wrap foo --noinhibit-exec --output-filetype=yaml -o %t2.out.undef 2>&1 | \ +#RUN: FileCheck %s -check-prefix=CHECKUNDEF +#CHECKWRAP: - name: main +#CHECKWRAP: references: +#CHECKWRAP: - kind: R_X86_64_PC32 +#CHECKWRAP: target: __wrap_foo +#CHECKWRAP: - name: __wrap_foo +#CHECKWRAP: references: +#CHECKWRAP: - kind: R_X86_64_PC32 +#CHECKWRAP: target: foo +#CHECKUNDEF: Undefined symbol: {{.*}}main.o: foo +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 5031C0E80000000031C05AC3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x0000000000000004 + Symbol: foo + Type: R_X86_64_PC32 + Addend: -4 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 00636C616E672076657273696F6E20332E372E3020287472756E6B203232383733392920286C6C766D2F7472756E6B203232383734382900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: 1400000000000000037A5200017810011B0C070890010000140000001C000000000000000C00000000410E1000000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .eh_frame + Relocations: + - Offset: 0x0000000000000020 + Symbol: .text + Type: R_X86_64_PC32 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .comment + Type: STT_SECTION + Section: .comment + - Name: .note.GNU-stack + Type: STT_SECTION + Section: .note.GNU-stack + - Name: .eh_frame + Type: STT_SECTION + Section: .eh_frame + Global: + - Name: main + Type: STT_FUNC + Section: .text + Size: 0x000000000000000C + - Name: foo +... +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 5031C0E80000000031C05AC3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x0000000000000004 + Symbol: __real_foo + Type: R_X86_64_PC32 + Addend: -4 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 00636C616E672076657273696F6E20332E372E3020287472756E6B203232383733392920286C6C766D2F7472756E6B203232383734382900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: 1400000000000000037A5200017810011B0C070890010000140000001C000000000000000C00000000410E1000000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .eh_frame + Relocations: + - Offset: 0x0000000000000020 + Symbol: .text + Type: R_X86_64_PC32 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .comment + Type: STT_SECTION + Section: .comment + - Name: .note.GNU-stack + Type: STT_SECTION + Section: .note.GNU-stack + - Name: .eh_frame + Type: STT_SECTION + Section: .eh_frame + Global: + - Name: __wrap_foo + Type: STT_FUNC + Section: .text + Size: 0x000000000000000C + - Name: __real_foo +... +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 31C0C3 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 00636C616E672076657273696F6E20332E372E3020287472756E6B203232383733392920286C6C766D2F7472756E6B203232383734382900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: 1400000000000000037A5200017810011B0C070890010000140000001C00000000000000030000000000000000000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .eh_frame + Relocations: + - Offset: 0x0000000000000020 + Symbol: .text + Type: R_X86_64_PC32 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .comment + Type: STT_SECTION + Section: .comment + - Name: .note.GNU-stack + Type: STT_SECTION + Section: .note.GNU-stack + - Name: .eh_frame + Type: STT_SECTION + Section: .eh_frame + Global: + - Name: foo + Type: STT_FUNC + Section: .text + Size: 0x0000000000000003 +... diff --git a/test/elf/x86-64-dynamic-relocs.test b/test/elf/x86-64-dynamic-relocs.test new file mode 100644 index 0000000000000..325693e3f7142 --- /dev/null +++ b/test/elf/x86-64-dynamic-relocs.test @@ -0,0 +1,26 @@ +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/relocs-dynamic.x86-64 \ +RUN: --output-filetype=yaml --noinhibit-exec | FileCheck %s + +path: <linker-internal> +defined-atoms: + - name: main + scope: global + content: [ E8, 00, 00, 00, 00, C3 ] + alignment: 2^4 + section-choice: custom-required + section-name: .text + references: + - kind: R_X86_64_PLT32 + offset: 1 + target: foo + addend: -4 + - name: foo + scope: global + content: [ C3 ] + alignment: 6 mod 2^4 + section-choice: custom-required + section-name: .text + +# Don't generate a PLT/GOT entry for a PLT32 relocation to a non-shared symbol. +CHECK-NOT: got +CHECK-NOT: stub diff --git a/test/elf/x86-64-dynamic.test b/test/elf/x86-64-dynamic.test new file mode 100644 index 0000000000000..4e24e954a20e6 --- /dev/null +++ b/test/elf/x86-64-dynamic.test @@ -0,0 +1,79 @@ +# Checks that linking an object file with a shared object creates the necessary +# PLT/GOT Entries +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/use-shared.x86-64 \ +RUN: %p/Inputs/shared.so-x86-64 --output-filetype=yaml -o %t1 --noinhibit-exec +RUN: FileCheck %s < %t1 + +RUN: lld -flavor gnu -target x86_64-linux %p/Inputs/use-shared-32s.x86-64 \ +RUN: %p/Inputs/shared.so-x86-64 --output-filetype=yaml --noinhibit-exec \ +RUN: | FileCheck %s --check-prefix=32S + +CHECK: - name: main +CHECK: scope: global +CHECK: references: +CHECK: - kind: R_X86_64_PC32 +CHECK: offset: 18 +CHECK: target: [[PLTNAME:[-a-zA-Z0-9_]+]] +CHECK: addend: -4 +CHECK: - kind: R_X86_64_GOTPCREL +CHECK: offset: 25 +CHECK: target: [[GOTNAME:[-a-zA-Z0-9_]+]] +CHECK: addend: -4 + + - name: .PLT0 +CHECK: type: stub +CHECK: content: [ FF, 35, 00, 00, 00, 00, FF, 25, 00, 00, 00, 00, +CHECK: 90, 90, 90, 90 ] +CHECK: alignment: 2^4 +CHECK: section-choice: custom-required +CHECK: section-name: .plt +CHECK: references: +CHECK: - kind: R_X86_64_PC32 +CHECK: offset: 2 + target: __got0 +CHECK: addend: -4 +CHECK: - kind: R_X86_64_PC32 +CHECK: offset: 8 + target: __got1 +CHECK: addend: -4 + - name: [[PLTNAME]] +CHECK: type: stub +CHECK: content: [ FF, 25, 00, 00, 00, 00, 68, 00, 00, 00, 00, E9, +CHECK: 00, 00, 00, 00 ] +CHECK: alignment: 2^4 +CHECK: section-choice: custom-required +CHECK: section-name: .plt +CHECK: references: +CHECK: - kind: R_X86_64_PC32 +CHECK: offset: 2 + target: __got_foo +CHECK: addend: -4 +CHECK: - kind: LLD_R_X86_64_GOTRELINDEX +CHECK: offset: 7 + target: __got_foo +CHECK: - kind: R_X86_64_PC32 +CHECK: offset: 12 + target: .PLT0 +CHECK: addend: -4 + +// Don't check the GOT and PLT names as they are only present in assert builds. +CHECK: type: got +CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ] +CHECK: section-choice: custom-required +CHECK: section-name: .got +CHECK: permissions: rw- +CHECK: references: +CHECK: - kind: R_X86_64_GLOB_DAT +CHECK: offset: 0 +CHECK: target: i + +CHECK:shared-library-atoms: +CHECK: - name: foo +CHECK: load-name: shared.so-x86-64 + +32S: name: main +32S: kind: R_X86_64_PC32 +32S: target: func +32S: kind: R_X86_64_32S +32S: kind: R_X86_64_PC32 +32S: type: stub diff --git a/test/elf/x86.test b/test/elf/x86.test new file mode 100644 index 0000000000000..6b68837193e6b --- /dev/null +++ b/test/elf/x86.test @@ -0,0 +1,38 @@ +# Source for input file: reloc-xb.x86 +# xb.S: +# .section .text, "ax", @progbits +# .align 0x4 +# .globl _start +# _start: +# .globl back +# back: +# call target +# +# Source for input file: reloc-xt.x86 +# xt.S: +# .section .text, "ax", @progbits +# +# .globl target +# target: +# call back +# +# Assembled with: "as --32" + +RUN: lld -flavor gnu -target i386 -e back --output-filetype=yaml %p/Inputs/reloc-xb.x86 %p/Inputs/reloc-xt.x86 | FileCheck %s -check-prefix x86-yaml + +x86-yaml: - name: back +x86-yaml: scope: global +x86-yaml: content: [ E8, FC, FF, FF, FF ] +x86-yaml: references: +x86-yaml: - kind: R_386_PC32 +x86-yaml: offset: 1 +x86-yaml: target: target + +x86-yaml: - name: target +x86-yaml: scope: global +x86-yaml: content: [ E8, FC, FF, FF, FF ] +x86-yaml: references: +x86-yaml: - kind: R_386_PC32 +x86-yaml: offset: 1 +x86-yaml: target: back + diff --git a/test/elf/x86_64-kinds.test b/test/elf/x86_64-kinds.test new file mode 100644 index 0000000000000..49586059d9536 --- /dev/null +++ b/test/elf/x86_64-kinds.test @@ -0,0 +1,23 @@ +REQUIRES: x86 + +RUN: lld -flavor gnu -target x86_64-linux -o %t1 %p/Inputs/relocs.x86-64 \ +RUN: -e _start -static +RUN: llvm-objdump -d %t1 | FileCheck %s -check-prefix=RELOCS + +RUN: lld -flavor gnu -target x86_64-linux --output-filetype=yaml -e _start -static \ +RUN: %p/Inputs/relocs.x86-64 | FileCheck %s -check-prefix=X86_64 + +RELOCS: ELF64-x86-64 + +// R_X86_64_32S +RELOCS: c7 04 25 +RELOCS-NOT: 00 00 00 00 +RELOCS: 05 00 00 00 movl + +// R_X86_64_PC32 +RELOCS: e8 +RELOCS-NOT: 00 00 00 00 +RELOCS: callq + +X86_64: R_X86_64_32S +X86_64: R_X86_64_PC32 diff --git a/test/lit.cfg b/test/lit.cfg new file mode 100644 index 0000000000000..5b49765f78943 --- /dev/null +++ b/test/lit.cfg @@ -0,0 +1,167 @@ +# -*- Python -*- + +import os +import platform +import re +import subprocess +import locale + +import lit.formats +import lit.util + +# Configuration file for the 'lit' test runner. + +# name: The name of this test suite. +config.name = 'lld' + +# testFormat: The test format to use to interpret tests. +# +# For now we require '&&' between commands, until they get globally killed and +# the test runner updated. +execute_external = (platform.system() != 'Windows' + or lit_config.getBashPath() not in [None, ""]) +config.test_format = lit.formats.ShTest(execute_external) + +# suffixes: A list of file extensions to treat as test files. +config.suffixes = ['.objtxt', '.test'] + +# test_source_root: The root path where tests are located. +config.test_source_root = os.path.dirname(__file__) + +# test_exec_root: The root path where tests should be run. +lld_obj_root = getattr(config, 'lld_obj_root', None) +if lld_obj_root is not None: + config.test_exec_root = os.path.join(lld_obj_root, 'test') + +# Set llvm_{src,obj}_root for use by others. +config.llvm_src_root = getattr(config, 'llvm_src_root', None) +config.llvm_obj_root = getattr(config, 'llvm_obj_root', None) + +# Tweak the PATH to include the tools dir and the scripts dir. +if lld_obj_root is not None: + llvm_tools_dir = getattr(config, 'llvm_tools_dir', None) + if not llvm_tools_dir: + lit_config.fatal('No LLVM tools dir set!') + path = os.path.pathsep.join((llvm_tools_dir, config.environment['PATH'])) + path = os.path.pathsep.join((os.path.join(getattr(config, 'llvm_src_root', None),'test','Scripts'),path)) + + config.environment['PATH'] = path + + llvm_libs_dir = getattr(config, 'llvm_libs_dir', None) + if not llvm_libs_dir: + lit_config.fatal('No LLVM libs dir set!') + path = os.path.pathsep.join((llvm_libs_dir, + config.environment.get('LD_LIBRARY_PATH',''))) + config.environment['LD_LIBRARY_PATH'] = path + + # Propagate LLVM_SRC_ROOT into the environment. + config.environment['LLVM_SRC_ROOT'] = getattr(config, 'llvm_src_root', '') + + # Propagate PYTHON_EXECUTABLE into the environment + config.environment['PYTHON_EXECUTABLE'] = getattr(config, 'python_executable', + '') +### + +# Check that the object root is known. +if config.test_exec_root is None: + # Otherwise, we haven't loaded the site specific configuration (the user is + # probably trying to run on a test file directly, and either the site + # configuration hasn't been created by the build system, or we are in an + # out-of-tree build situation). + + # Check for 'lld_site_config' user parameter, and use that if available. + site_cfg = lit_config.params.get('lld_site_config', None) + if site_cfg and os.path.exists(site_cfg): + lit_config.load_config(config, site_cfg) + raise SystemExit + + # Try to detect the situation where we are using an out-of-tree build by + # looking for 'llvm-config'. + # + # FIXME: I debated (i.e., wrote and threw away) adding logic to + # automagically generate the lit.site.cfg if we are in some kind of fresh + # build situation. This means knowing how to invoke the build system though, + # and I decided it was too much magic. We should solve this by just having + # the .cfg files generated during the configuration step. + + llvm_config = lit.util.which('llvm-config', config.environment['PATH']) + if not llvm_config: + lit_config.fatal('No site specific configuration available!') + + # Get the source and object roots. + llvm_src_root = lit.util.capture(['llvm-config', '--src-root']).strip() + llvm_obj_root = lit.util.capture(['llvm-config', '--obj-root']).strip() + lld_src_root = os.path.join(llvm_src_root, "tools", "lld") + lld_obj_root = os.path.join(llvm_obj_root, "tools", "lld") + + # Validate that we got a tree which points to here, using the standard + # tools/lld layout. + this_src_root = os.path.dirname(config.test_source_root) + if os.path.realpath(lld_src_root) != os.path.realpath(this_src_root): + lit_config.fatal('No site specific configuration available!') + + # Check that the site specific configuration exists. + site_cfg = os.path.join(lld_obj_root, 'test', 'lit.site.cfg') + if not os.path.exists(site_cfg): + lit_config.fatal( + 'No site specific configuration available! You may need to ' + 'run "make test" in your lld build directory.') + + # Okay, that worked. Notify the user of the automagic, and reconfigure. + lit_config.note('using out-of-tree build at %r' % lld_obj_root) + lit_config.load_config(config, site_cfg) + raise SystemExit + +# When running under valgrind, we mangle '-vg' onto the end of the triple so we +# can check it with XFAIL and XTARGET. +if lit_config.useValgrind: + config.target_triple += '-vg' + +# Shell execution +if platform.system() not in ['Windows'] or lit_config.getBashPath() != '': + config.available_features.add('shell') + +# Running on Darwin OS +if platform.system() in ['Darwin']: + config.available_features.add('system-linker-mach-o') + +# Running on ELF based *nix +if platform.system() in ['FreeBSD', 'Linux']: + config.available_features.add('system-linker-elf') + +# llvm-config knows whether it is compiled with asserts (and) +# whether we are operating in release/debug mode. +import subprocess +try: + llvm_config_cmd = \ + subprocess.Popen([os.path.join(llvm_tools_dir, 'llvm-config'), + '--build-mode', '--assertion-mode', '--targets-built'], + stdout = subprocess.PIPE) +except OSError as why: + print("Could not find llvm-config in " + llvm_tools_dir) + exit(42) + +llvm_config_output = llvm_config_cmd.stdout.read().decode('utf_8') +llvm_config_output_list = llvm_config_output.split("\n") + +if re.search(r'DEBUG', llvm_config_output_list[0]): + config.available_features.add('debug') +if re.search(r'ON', llvm_config_output_list[1]): + config.available_features.add('asserts') +if re.search(r'ARM', llvm_config_output_list[2]): + config.available_features.add('arm') +if re.search(r'Mips', llvm_config_output_list[2]): + config.available_features.add('mips') +if re.search(r'X86', llvm_config_output_list[2]): + config.available_features.add('x86') +llvm_config_cmd.wait() + +# Check if Windows resource file compiler exists. +cvtres = lit.util.which('cvtres', config.environment['PATH']) +rc = lit.util.which('rc', config.environment['PATH']) +if cvtres and rc: + config.available_features.add('winres') + +# Check if "lib.exe" command exists. +if lit.util.which('lib.exe', config.environment['PATH']): + config.available_features.add('winlib') diff --git a/test/lit.site.cfg.in b/test/lit.site.cfg.in new file mode 100644 index 0000000000000..0eeb889d1b202 --- /dev/null +++ b/test/lit.site.cfg.in @@ -0,0 +1,22 @@ +## Autogenerated by LLVM/lld configuration. +# Do not edit! +config.llvm_src_root = "@LLVM_SOURCE_DIR@" +config.llvm_obj_root = "@LLVM_BINARY_DIR@" +config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" +config.llvm_libs_dir = "@LLVM_LIBS_DIR@" +config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@" +config.lld_obj_root = "@LLD_BINARY_DIR@" +config.target_triple = "@TARGET_TRIPLE@" +config.python_executable = "@PYTHON_EXECUTABLE@" + +# Support substitution of the tools and libs dirs with user parameters. This is +# used when we can't determine the tool dir at configuration time. +try: + config.llvm_tools_dir = config.llvm_tools_dir % lit_config.params + config.llvm_libs_dir = config.llvm_libs_dir % lit_config.params +except KeyError as e: + key, = e.args + lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key)) + +# Let the main config do the real work. +lit_config.load_config(config, "@LLD_SOURCE_DIR@/test/lit.cfg") diff --git a/test/mach-o/Inputs/DependencyDump.py b/test/mach-o/Inputs/DependencyDump.py new file mode 100755 index 0000000000000..0f4d49d6fb9a8 --- /dev/null +++ b/test/mach-o/Inputs/DependencyDump.py @@ -0,0 +1,30 @@ +# -*- Python -*- + + +# +# Dump out Xcode binary dependency file. +# + +import sys + +f = open(sys.argv[1], "rb") +byte = f.read(1) +while byte != b'': + if byte == b'\000': + sys.stdout.write("linker-vers: ") + elif byte == b'\020': + sys.stdout.write("input-file: ") + elif byte == b'\021': + sys.stdout.write("not-found: ") + elif byte == b'\100': + sys.stdout.write("output-file: ") + byte = f.read(1) + while byte != b'\000': + if byte != b'\012': + sys.stdout.write(byte.decode("ascii")) + byte = f.read(1) + sys.stdout.write("\n") + byte = f.read(1) + +f.close() + diff --git a/test/mach-o/Inputs/bar.yaml b/test/mach-o/Inputs/bar.yaml new file mode 100644 index 0000000000000..5605e67e7c352 --- /dev/null +++ b/test/mach-o/Inputs/bar.yaml @@ -0,0 +1,18 @@ + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0xC3 ] +global-symbols: + - name: _bar + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 diff --git a/test/mach-o/Inputs/exported_symbols_list.exp b/test/mach-o/Inputs/exported_symbols_list.exp new file mode 100644 index 0000000000000..ff66533424721 --- /dev/null +++ b/test/mach-o/Inputs/exported_symbols_list.exp @@ -0,0 +1,6 @@ +# +# For use with exported_symbols_list.yaml +# +_foo +_b + diff --git a/test/mach-o/Inputs/full.filelist b/test/mach-o/Inputs/full.filelist new file mode 100644 index 0000000000000..abf98b6333776 --- /dev/null +++ b/test/mach-o/Inputs/full.filelist @@ -0,0 +1,3 @@ +/foo/bar/a.o +/foo/bar/b.o +/foo/x.a diff --git a/test/mach-o/Inputs/lib-search-paths/usr/lib/libmyshared.dylib b/test/mach-o/Inputs/lib-search-paths/usr/lib/libmyshared.dylib Binary files differnew file mode 100755 index 0000000000000..71185fbdf7360 --- /dev/null +++ b/test/mach-o/Inputs/lib-search-paths/usr/lib/libmyshared.dylib diff --git a/test/mach-o/Inputs/lib-search-paths/usr/lib/libmystatic.a b/test/mach-o/Inputs/lib-search-paths/usr/lib/libmystatic.a Binary files differnew file mode 100644 index 0000000000000..b12062941f376 --- /dev/null +++ b/test/mach-o/Inputs/lib-search-paths/usr/lib/libmystatic.a diff --git a/test/mach-o/Inputs/lib-search-paths/usr/local/lib/file.o b/test/mach-o/Inputs/lib-search-paths/usr/local/lib/file.o Binary files differnew file mode 100644 index 0000000000000..f9a923d37db38 --- /dev/null +++ b/test/mach-o/Inputs/lib-search-paths/usr/local/lib/file.o diff --git a/test/mach-o/Inputs/libSystem.yaml b/test/mach-o/Inputs/libSystem.yaml new file mode 100644 index 0000000000000..2a7f46381dcc1 --- /dev/null +++ b/test/mach-o/Inputs/libSystem.yaml @@ -0,0 +1,13 @@ +# +# For use by test cases that create dynamic output types which may needs stubs +# and therefore will need a dylib definition of dyld_stub_binder. +# + +--- +shared-library-atoms: + - name: dyld_stub_binder + load-name: /usr/lib/libSystem.B.dylib + type: code + size: 0 + +... diff --git a/test/mach-o/Inputs/libbar.a b/test/mach-o/Inputs/libbar.a Binary files differnew file mode 100644 index 0000000000000..64cae6c749eee --- /dev/null +++ b/test/mach-o/Inputs/libbar.a diff --git a/test/mach-o/Inputs/libfoo.a b/test/mach-o/Inputs/libfoo.a Binary files differnew file mode 100644 index 0000000000000..21194efbabf92 --- /dev/null +++ b/test/mach-o/Inputs/libfoo.a diff --git a/test/mach-o/Inputs/order_file-basic.order b/test/mach-o/Inputs/order_file-basic.order new file mode 100644 index 0000000000000..0ac90cb79da76 --- /dev/null +++ b/test/mach-o/Inputs/order_file-basic.order @@ -0,0 +1,11 @@ + +# input file for order_file-basic.yaml + +_func2 +libfoo.a(foo.o):_foo # tests file specific ordering within archive +i386:_func3 # wrong arch, so ignored +armv7:_func3 # wrong arch, so ignored +_func1 +_notfound # unknown symbol silently ignored +_data3 # data symbols should be orderable + diff --git a/test/mach-o/Inputs/partial.filelist b/test/mach-o/Inputs/partial.filelist new file mode 100644 index 0000000000000..281581bf00fc0 --- /dev/null +++ b/test/mach-o/Inputs/partial.filelist @@ -0,0 +1,3 @@ +bar/a.o +bar/b.o +x.a diff --git a/test/mach-o/Inputs/use-dylib-install-names.yaml b/test/mach-o/Inputs/use-dylib-install-names.yaml new file mode 100644 index 0000000000000..cec2559f2435e --- /dev/null +++ b/test/mach-o/Inputs/use-dylib-install-names.yaml @@ -0,0 +1,28 @@ +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0xE8, 0x00, 0x00, 0x00, + 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x00, + 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, + 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xE9, 0x00, + 0x00, 0x00, 0x00 ] +global-symbols: + - name: _foo + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: _myGlobal + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 diff --git a/test/mach-o/PIE.yaml b/test/mach-o/PIE.yaml new file mode 100644 index 0000000000000..2e50951a4544b --- /dev/null +++ b/test/mach-o/PIE.yaml @@ -0,0 +1,44 @@ +# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t && \ +# RUN: llvm-objdump -macho -private-headers %t | FileCheck %s +# +# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -pie -o %t\ +# RUN: && llvm-objdump -macho -private-headers %t | FileCheck %s +# +# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -no_pie -o %t\ +# RUN: && llvm-objdump -macho -private-headers %t \ +# RUN: | FileCheck --check-prefix=CHECK_NO_PIE %s +# +# Test various PIE options. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0xC3 ] +global-symbols: + - name: _main + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + +--- !mach-o +arch: x86_64 +file-type: MH_DYLIB +install-name: /usr/lib/libSystem.B.dylib +exports: + - name: dyld_stub_binder + +... + +# CHECK: MH_MAGIC_64 {{[0-9a-zA-Z _]+}} TWOLEVEL PIE +# CHECK_NO_PIE-NOT: MH_MAGIC_64 {{[0-9a-zA-Z _]+}} TWOLEVEL PIE diff --git a/test/mach-o/align_text.yaml b/test/mach-o/align_text.yaml new file mode 100644 index 0000000000000..5ddbf911b9e51 --- /dev/null +++ b/test/mach-o/align_text.yaml @@ -0,0 +1,45 @@ +# RUN: lld -flavor darwin -arch x86_64 -r %s -o %t -print_atoms | FileCheck %s +# RUN: lld -flavor darwin -arch x86_64 -r %t -o %t2 -print_atoms | FileCheck %s +# +# Test that alignment info round trips through -r +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 4 + address: 0x0000000000000000 + content: [ 0x90, 0x90, 0x90, 0xC3, 0xC3, 0xC3 ] +local-symbols: + - name: _f1 + type: N_SECT + sect: 1 + value: 0x0000000000000003 + - name: _f2 + type: N_SECT + sect: 1 + value: 0x0000000000000004 + - name: _f3 + type: N_SECT + sect: 1 + value: 0x0000000000000005 +... + +# CHECK: defined-atoms: +# CHECK: - content: [ 90, 90, 90 ] +# CHECK: alignment: 2^4 +# CHECK: - name: _f1 +# CHECK: content: [ C3 ] +# CHECK: alignment: 3 mod 2^4 +# CHECK: - name: _f2 +# CHECK: content: [ C3 ] +# CHECK: alignment: 4 mod 2^4 +# CHECK: - name: _f3 +# CHECK: content: [ C3 ] +# CHECK: alignment: 5 mod 2^4 diff --git a/test/mach-o/arm-interworking-movw.yaml b/test/mach-o/arm-interworking-movw.yaml new file mode 100644 index 0000000000000..59d2f0ddd3d9c --- /dev/null +++ b/test/mach-o/arm-interworking-movw.yaml @@ -0,0 +1,393 @@ +# REQUIRES: arm +# RUN: lld -flavor darwin -arch armv7 -r -print_atoms %s -o %t | FileCheck %s +# RUN: lld -flavor darwin -arch armv7 -dylib -print_atoms %t -o %t2 \ +# RUN: %p/Inputs/libSystem.yaml -sectalign __TEXT __text 0x1000 | FileCheck %s +# RUN: llvm-objdump -d -macho %t2 | FileCheck -check-prefix=CODE %s +# +# Test thumb and arm branches round trip through -r. +# Test movw/movt pairs have low bit set properly for thumb vs arm. +# +# + +--- !mach-o +arch: armv7 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 2 + address: 0x0000000000000000 + content: [ 0x40, 0xF2, 0x25, 0x00, 0xC0, 0xF2, 0x00, 0x00, + 0x40, 0xF2, 0x01, 0x01, 0xC0, 0xF2, 0x00, 0x01, + 0x40, 0xF2, 0x4E, 0x02, 0xC0, 0xF2, 0x00, 0x02, + 0x40, 0xF2, 0x2A, 0x03, 0xC0, 0xF2, 0x00, 0x03, + 0x78, 0x44, 0x70, 0x47, 0x70, 0x47, 0x25, 0x00, + 0x00, 0xE3, 0x00, 0x00, 0x40, 0xE3, 0xD7, 0x1F, + 0x0F, 0xE3, 0xFF, 0x1F, 0x4F, 0xE3, 0x4E, 0x20, + 0x00, 0xE3, 0x00, 0x20, 0x40, 0xE3, 0x00, 0x30, + 0x00, 0xE3, 0x00, 0x30, 0x40, 0xE3, 0x0F, 0x00, + 0x80, 0xE0, 0x1E, 0xFF, 0x2F, 0xE1, 0x1E, 0xFF, + 0x2F, 0xE1 ] + relocations: + - offset: 0x00000042 + scattered: true + type: ARM_RELOC_HALF_SECTDIFF + length: 1 + pc-rel: false + value: 0x0000004E + - offset: 0x00000000 + scattered: true + type: ARM_RELOC_PAIR + length: 1 + pc-rel: false + value: 0x00000046 + - offset: 0x0000003E + scattered: true + type: ARM_RELOC_HALF_SECTDIFF + length: 0 + pc-rel: false + value: 0x0000004E + - offset: 0x00000000 + scattered: true + type: ARM_RELOC_PAIR + length: 0 + pc-rel: false + value: 0x00000046 + - offset: 0x0000003A + type: ARM_RELOC_HALF + length: 1 + pc-rel: false + extern: false + symbol: 1 + - offset: 0x0000004E + type: ARM_RELOC_PAIR + length: 1 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x00000036 + type: ARM_RELOC_HALF + length: 0 + pc-rel: false + extern: false + symbol: 1 + - offset: 0x00000000 + type: ARM_RELOC_PAIR + length: 0 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x00000032 + scattered: true + type: ARM_RELOC_HALF_SECTDIFF + length: 1 + pc-rel: false + value: 0x00000024 + - offset: 0x0000FFD6 + scattered: true + type: ARM_RELOC_PAIR + length: 1 + pc-rel: false + value: 0x00000046 + - offset: 0x0000002E + scattered: true + type: ARM_RELOC_HALF_SECTDIFF + length: 0 + pc-rel: false + value: 0x00000024 + - offset: 0x0000FFFF + scattered: true + type: ARM_RELOC_PAIR + length: 0 + pc-rel: false + value: 0x00000046 + - offset: 0x0000002A + type: ARM_RELOC_HALF + length: 1 + pc-rel: false + extern: false + symbol: 1 + - offset: 0x00000025 + type: ARM_RELOC_PAIR + length: 1 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x00000026 + type: ARM_RELOC_HALF + length: 0 + pc-rel: false + extern: false + symbol: 1 + - offset: 0x00000000 + type: ARM_RELOC_PAIR + length: 0 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x0000001C + scattered: true + type: ARM_RELOC_HALF_SECTDIFF + length: 3 + pc-rel: false + value: 0x0000004E + - offset: 0x0000002A + scattered: true + type: ARM_RELOC_PAIR + length: 3 + pc-rel: false + value: 0x00000020 + - offset: 0x00000018 + scattered: true + type: ARM_RELOC_HALF_SECTDIFF + length: 2 + pc-rel: false + value: 0x0000004E + - offset: 0x00000000 + scattered: true + type: ARM_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x00000020 + - offset: 0x00000014 + type: ARM_RELOC_HALF + length: 3 + pc-rel: false + extern: false + symbol: 1 + - offset: 0x0000004E + type: ARM_RELOC_PAIR + length: 3 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x00000010 + type: ARM_RELOC_HALF + length: 2 + pc-rel: false + extern: false + symbol: 1 + - offset: 0x00000000 + type: ARM_RELOC_PAIR + length: 2 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x0000000C + scattered: true + type: ARM_RELOC_HALF_SECTDIFF + length: 3 + pc-rel: false + value: 0x00000024 + - offset: 0x00000000 + scattered: true + type: ARM_RELOC_PAIR + length: 3 + pc-rel: false + value: 0x00000020 + - offset: 0x00000008 + scattered: true + type: ARM_RELOC_HALF_SECTDIFF + length: 2 + pc-rel: false + value: 0x00000024 + - offset: 0x00000000 + scattered: true + type: ARM_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x00000020 + - offset: 0x00000004 + type: ARM_RELOC_HALF + length: 3 + pc-rel: false + extern: false + symbol: 1 + - offset: 0x00000025 + type: ARM_RELOC_PAIR + length: 3 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x00000000 + type: ARM_RELOC_HALF + length: 2 + pc-rel: false + extern: false + symbol: 1 + - offset: 0x00000000 + type: ARM_RELOC_PAIR + length: 2 + pc-rel: false + extern: false + symbol: 16777215 +local-symbols: + - name: _t1 + type: N_SECT + sect: 1 + desc: [ N_ARM_THUMB_DEF ] + value: 0x0000000000000000 + - name: _t2 + type: N_SECT + sect: 1 + desc: [ N_ARM_THUMB_DEF ] + value: 0x0000000000000024 + - name: _a2 + type: N_SECT + sect: 1 + value: 0x000000000000004E + - name: _a1 + type: N_SECT + sect: 1 + value: 0x0000000000000026 +... + +# CHECK: defined-atoms: +# CHECK: - name: _t1 +# CHECK: references: +# CHECK: - kind: modeThumbCode +# CHECK: offset: 0 +# CHECK: target: _t1 +# CHECK: - kind: thumb_movw +# CHECK: offset: 0 +# CHECK: target: _t2 +# CHECK-NOT: addend: +# CHECK: - kind: thumb_movt +# CHECK: offset: 4 +# CHECK: target: _t2 +# CHECK-NOT: addend: +# CHECK: - kind: thumb_movw_funcRel +# CHECK: offset: 8 +# CHECK: target: _t2 +# CHECK: addend: -36 +# CHECK: - kind: thumb_movt_funcRel +# CHECK: offset: 12 +# CHECK: target: _t2 +# CHECK: addend: -36 +# CHECK: - kind: thumb_movw +# CHECK: offset: 16 +# CHECK: target: _a2 +# CHECK-NOT: addend: +# CHECK: - kind: thumb_movt +# CHECK: offset: 20 +# CHECK: target: _a2 +# CHECK-NOT: addend: +# CHECK: - kind: thumb_movw_funcRel +# CHECK: offset: 24 +# CHECK: target: _a2 +# CHECK: addend: -36 +# CHECK: - kind: thumb_movt_funcRel +# CHECK: offset: 28 +# CHECK: target: _a2 +# CHECK: addend: -36 +# CHECK: - name: _t2 +# CHECK: references: +# CHECK: - kind: modeThumbCode +# CHECK: offset: 0 +# CHECK: target: _t2 +# CHECK: - name: _a1 +# CHECK: references: +# CHECK: - kind: arm_movw +# CHECK: offset: 0 +# CHECK: target: _t2 +# CHECK-NOT: addend: +# CHECK: - kind: arm_movt +# CHECK: offset: 4 +# CHECK: target: _t2 +# CHECK-NOT: addend: +# CHECK: - kind: arm_movw_funcRel +# CHECK: offset: 8 +# CHECK: target: _t2 +# CHECK: addend: -40 +# CHECK: - kind: arm_movt_funcRel +# CHECK: offset: 12 +# CHECK: target: _t2 +# CHECK: addend: -40 +# CHECK: - kind: arm_movw +# CHECK: offset: 16 +# CHECK: target: _a2 +# CHECK-NOT: addend: +# CHECK: - kind: arm_movt +# CHECK: offset: 20 +# CHECK: target: _a2 +# CHECK-NOT: addend: +# CHECK: - kind: arm_movw_funcRel +# CHECK: offset: 24 +# CHECK: target: _a2 +# CHECK: addend: -40 +# CHECK: - kind: arm_movt_funcRel +# CHECK: offset: 28 +# CHECK: target: _a2 +# CHECK: addend: -40 +# CHECK: - name: _a2 + + +# CODE: _t1: +# CODE-NEXT: movw r0, #4133 +# CODE-NEXT: movt r0, #0 +# CODE-NEXT: movw r1, #1 +# CODE-NEXT: movt r1, #0 +# CODE-NEXT: movw r2, _a2 +# CODE-NEXT: movt r2, #0 +# CODE-NEXT: movw r3, #42 +# CODE-NEXT: movt r3, #0 + + +# CODE: _a1: +# CODE-NEXT: movw r0, #4133 +# CODE-NEXT: movt r0, #0 +# CODE-NEXT: movw r1, #65495 +# CODE-NEXT: movt r1, #65535 +# CODE-NEXT: movw r2, _a2 +# CODE-NEXT: movt r2, #0 +# CODE-NEXT: movw r3, #0 +# CODE-NEXT: movt r3, #0 + + + +# .syntax unified +# .align 2 +# +# .code 16 +# .thumb_func _t1 +#_t1: +# movw r0, :lower16:(_t2) +# movt r0, :upper16:(_t2) +# movw r1, :lower16:(_t2-(L0+4)) +# movt r1, :upper16:(_t2-(L0+4)) +# movw r2, :lower16:(_a2) +# movt r2, :upper16:(_a2) +# movw r3, :lower16:(_a2-(L0+4)) +# movt r3, :upper16:(_a2-(L0+4)) +#L0: +# add r0, pc +# bx lr +# +# +# .code 16 +# .thumb_func _t2 +#_t2: +# bx lr +# +# +# +# .code 32 +#_a1: +# movw r0, :lower16:(_t2) +# movt r0, :upper16:(_t2) +# movw r1, :lower16:(_t2-(L1+8)) +# movt r1, :upper16:(_t2-(L1+8)) +# movw r2, :lower16:(_a2) +# movt r2, :upper16:(_a2) +# movw r3, :lower16:(_a2-(L1+8)) +# movt r3, :upper16:(_a2-(L1+8)) +#L1: +# add r0, pc +# bx lr +# +#_a2: +# bx lr + diff --git a/test/mach-o/arm-interworking.yaml b/test/mach-o/arm-interworking.yaml new file mode 100644 index 0000000000000..f7e04e65c9f01 --- /dev/null +++ b/test/mach-o/arm-interworking.yaml @@ -0,0 +1,362 @@ +# RUN: lld -flavor darwin -arch armv7 -r -print_atoms %s -o %t | FileCheck %s \ +# RUN: && lld -flavor darwin -arch armv7 -dylib -print_atoms \ +# RUN: %p/Inputs/libSystem.yaml %t -o %t2 | FileCheck %s \ +# RUN: && macho-dump --dump-section-data %t2 | FileCheck -check-prefix=CODE %s +# +# Test thumb and arm branches round trip through -r. +# Test bl/blx instructions are fixed up properly. +# +# + +--- !mach-o +arch: armv7 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 2 + address: 0x0000000000000000 + content: [ 0xFF, 0xF7, 0xFE, 0xFF, 0xC0, 0x46, 0xFF, 0xF7, + 0xFC, 0xEF, 0xC0, 0x46, 0xFF, 0xF7, 0xF8, 0xEF, + 0xFF, 0xF7, 0xF6, 0xFF, 0xC0, 0x46, 0xFF, 0xF7, + 0xF3, 0xFF, 0xC0, 0x46, 0x00, 0xF0, 0x06, 0xE8, + 0xC0, 0x46, 0x00, 0xF0, 0x03, 0xF8, 0x00, 0xF0, + 0x02, 0xF8, 0x70, 0x47, 0x70, 0x47, 0x70, 0x47 ] + relocations: + - offset: 0x00000026 + type: ARM_THUMB_RELOC_BR22 + length: 2 + pc-rel: true + extern: false + symbol: 1 + - offset: 0x00000022 + type: ARM_THUMB_RELOC_BR22 + length: 2 + pc-rel: true + extern: false + symbol: 1 + - offset: 0x0000001C + type: ARM_THUMB_RELOC_BR22 + length: 2 + pc-rel: true + extern: false + symbol: 1 + - offset: 0x00000016 + type: ARM_THUMB_RELOC_BR22 + length: 2 + pc-rel: true + extern: false + symbol: 1 + - offset: 0x00000010 + type: ARM_THUMB_RELOC_BR22 + length: 2 + pc-rel: true + extern: false + symbol: 1 + - offset: 0x0000000C + type: ARM_THUMB_RELOC_BR22 + length: 2 + pc-rel: true + extern: true + symbol: 5 + - offset: 0x00000006 + type: ARM_THUMB_RELOC_BR22 + length: 2 + pc-rel: true + extern: true + symbol: 5 + - offset: 0x00000000 + type: ARM_THUMB_RELOC_BR22 + length: 2 + pc-rel: true + extern: true + symbol: 4 + - segment: __DATA + section: __data + type: S_REGULAR + attributes: [ ] + address: 0x0000000000000030 + content: [ 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000004 + type: ARM_RELOC_VANILLA + length: 2 + pc-rel: false + extern: true + symbol: 4 + - offset: 0x00000000 + type: ARM_RELOC_VANILLA + length: 2 + pc-rel: false + extern: false + symbol: 1 +local-symbols: + - name: _t3 + type: N_SECT + sect: 1 + desc: [ N_ARM_THUMB_DEF ] + value: 0x000000000000002E + - name: _d1 + type: N_SECT + sect: 2 + value: 0x0000000000000030 +global-symbols: + - name: _t1 + type: N_SECT + scope: [ N_EXT ] + sect: 1 + desc: [ N_ARM_THUMB_DEF ] + value: 0x0000000000000000 + - name: _t2 + type: N_SECT + scope: [ N_EXT ] + sect: 1 + desc: [ N_ARM_THUMB_DEF ] + value: 0x000000000000002C +undefined-symbols: + - name: _a1 + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: _a2 + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + +--- !mach-o +arch: armv7 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 2 + address: 0x0000000000000000 + content: [ 0xFE, 0xFF, 0xFF, 0xEB, 0x02, 0x00, 0x00, 0xFA, + 0xFC, 0xFF, 0xFF, 0xEB, 0xFB, 0xFF, 0xFF, 0xFA, + 0x1E, 0xFF, 0x2F, 0xE1, 0x1E, 0xFF, 0x2F, 0xE1 ] + relocations: + - offset: 0x0000000C + type: ARM_RELOC_BR24 + length: 2 + pc-rel: true + extern: true + symbol: 4 + - offset: 0x00000008 + type: ARM_RELOC_BR24 + length: 2 + pc-rel: true + extern: true + symbol: 3 + - offset: 0x00000004 + type: ARM_RELOC_BR24 + length: 2 + pc-rel: true + extern: false + symbol: 1 + - offset: 0x00000000 + type: ARM_RELOC_BR24 + length: 2 + pc-rel: true + extern: false + symbol: 1 + - segment: __DATA + section: __data + type: S_REGULAR + attributes: [ ] + address: 0x0000000000000018 + content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000004 + type: ARM_RELOC_VANILLA + length: 2 + pc-rel: false + extern: false + symbol: 1 + - offset: 0x00000000 + type: ARM_RELOC_VANILLA + length: 2 + pc-rel: false + extern: true + symbol: 3 +local-symbols: + - name: _d2 + type: N_SECT + sect: 2 + value: 0x0000000000000018 +global-symbols: + - name: _a1 + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + - name: _a2 + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000014 +undefined-symbols: + - name: _t1 + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: _t2 + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 +... + + +# CHECK: defined-atoms: +# CHECK: - name: _d1 +# CHECK: type: data +# CHECK: references: +# CHECK: - kind: pointer32 +# CHECK: offset: 0 +# CHECK: target: _t2 +# CHECK: - kind: pointer32 +# CHECK: offset: 4 +# CHECK: target: _a1 +# CHECK: - name: _d2 +# CHECK: type: data +# CHECK: references: +# CHECK: - kind: pointer32 +# CHECK: offset: 0 +# CHECK: target: _t1 +# CHECK: - kind: pointer32 +# CHECK: offset: 4 +# CHECK: target: _a1 +# CHECK: - name: _t1 +# CHECK: scope: global +# CHECK: references: +# CHECK: - kind: modeThumbCode +# CHECK: offset: 0 +# CHECK: target: _t1 +# CHECK: - kind: thumb_bl22 +# CHECK: offset: 0 +# CHECK: target: _a1 +# CHECK: - kind: thumb_bl22 +# CHECK: offset: 6 +# CHECK: target: _a2 +# CHECK: - kind: thumb_bl22 +# CHECK: offset: 12 +# CHECK: target: _a2 +# CHECK: - kind: thumb_bl22 +# CHECK: offset: 16 +# CHECK: target: _t1 +# CHECK: - kind: thumb_bl22 +# CHECK: offset: 22 +# CHECK: target: _t1 +# CHECK: - kind: thumb_bl22 +# CHECK: offset: 28 +# CHECK: target: _t2 +# CHECK: - kind: thumb_bl22 +# CHECK: offset: 34 +# CHECK: target: _t2 +# CHECK: - kind: thumb_bl22 +# CHECK: offset: 38 +# CHECK: target: _t3 +# CHECK: - name: _t2 +# CHECK: scope: global +# CHECK: content: [ 70, 47 ] +# CHECK: references: +# CHECK: - kind: modeThumbCode +# CHECK: offset: 0 +# CHECK: target: _t2 +# CHECK: - name: _t3 +# CHECK: content: [ 70, 47 ] +# CHECK: references: +# CHECK: - kind: modeThumbCode +# CHECK: offset: 0 +# CHECK: target: _t3 +# CHECK: - name: _a1 +# CHECK: scope: global +# CHECK: references: +# CHECK: - kind: arm_bl24 +# CHECK: offset: 0 +# CHECK: target: _a1 +# CHECK: - kind: arm_bl24 +# CHECK: offset: 4 +# CHECK: target: _a2 +# CHECK: - kind: arm_bl24 +# CHECK: offset: 8 +# CHECK: target: _t1 +# CHECK: - kind: arm_bl24 +# CHECK: offset: 12 +# CHECK: target: _t2 +# CHECK: - name: _a2 +# CHECK: scope: global + + +# CODE: (('section_name', '__text\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') +# CODE: ('segment_name', '__TEXT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') +# CODE: ('_section_data', '00f016e8 c04600f0 1ee8c046 00f01ae8 fff7f6ff c046fff7 f3ffc046 00f006f8 c04600f0 03f800f0 02f87047 70477047 feffffeb 020000eb f0fffffa fafffffa 1eff2fe1 1eff2fe1') +# When we get a good mach-o disassembler the above __text section content check can be change to be symbolic. + +# CODE: (('section_name', '__data\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') +# CODE: ('segment_name', '__DATA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') +# CODE: ('_section_data', '{{[0-9a-f]}}{{[13579bdf]}}{{[0-9a-f]+}} {{[0-9a-f]}}{{[02468ade]}}{{[0-9a-f]+}} {{[0-9a-f]}}{{[13579bdf]}}{{[0-9a-f]+}} {{[0-9a-f]}}{{[02468ade]}}{{[0-9a-f]+}}') +# Verify the low (thumb) bit is set on the first and third pointers but not the second and fourth. + + + +# Input file one: +# +# .align 2 +# .code 16 +# .globl _t1 +# .thumb_func _t1 +#_t1: +# bl _a1 +# nop +# blx _a2 +# nop +# blx _a2 +# bl _t1 +# nop +# bl _t1 +# nop +# blx _t2 +# nop +# blx _t2 +# bx lr +# +# .globl _t2 +# .thumb_func _t2 +#_t2: +# bx lr +# +# .data +#_d1: .long _t2 +# .long _a1 + + + +# Input file two: +# +# .align 2 +# .code 32 +# .globl _a1 +#_a1: +# bl _a1 +# blx _a2 +# bl _t1 +# blx _t2 +# bx lr +# +# .globl _a2 +#_a2: +# bx lr +# +# .data +#_d2: .long _t1 +# .long _a1 + + + +
\ No newline at end of file diff --git a/test/mach-o/arm-shims.yaml b/test/mach-o/arm-shims.yaml new file mode 100644 index 0000000000000..68a401ca78e82 --- /dev/null +++ b/test/mach-o/arm-shims.yaml @@ -0,0 +1,179 @@ +# RUN: lld -flavor darwin -arch armv7 %s -dylib %p/Inputs/libSystem.yaml -o %t +# RUN: macho-dump --dump-section-data %t | FileCheck %s +# +# Test b from arm to thumb or vice versa has shims added.s +# +# + +--- !mach-o +arch: armv7 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 2 + address: 0x0000000000000000 + content: [ 0x00, 0xBF, 0xFF, 0xF7, 0xFE, 0xEF, 0xFF, 0xF7, + 0xFB, 0xBF, 0x00, 0x00, 0x00, 0xF0, 0x20, 0xE3, + 0xFA, 0xFF, 0xFF, 0xFA, 0xF9, 0xFF, 0xFF, 0xEA ] + relocations: + - offset: 0x00000014 + type: ARM_RELOC_BR24 + length: 2 + pc-rel: true + extern: true + symbol: 3 + - offset: 0x00000010 + type: ARM_RELOC_BR24 + length: 2 + pc-rel: true + extern: true + symbol: 3 + - offset: 0x00000006 + type: ARM_THUMB_RELOC_BR22 + length: 2 + pc-rel: true + extern: true + symbol: 2 + - offset: 0x00000002 + type: ARM_THUMB_RELOC_BR22 + length: 2 + pc-rel: true + extern: true + symbol: 2 +global-symbols: + - name: _a1 + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x000000000000000C + - name: _t1 + type: N_SECT + scope: [ N_EXT ] + sect: 1 + desc: [ N_ARM_THUMB_DEF ] + value: 0x0000000000000000 +undefined-symbols: + - name: _a2 + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: _t2 + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + + +--- !mach-o +arch: armv7 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 2 + address: 0x0000000000000000 + content: [ 0x00, 0xBF, 0xFF, 0xF7, 0xFE, 0xEF, 0xFF, 0xF7, + 0xFB, 0xBF, 0x00, 0x00, 0x00, 0xF0, 0x20, 0xE3, + 0xFA, 0xFF, 0xFF, 0xFA, 0xF9, 0xFF, 0xFF, 0xEA ] + relocations: + - offset: 0x00000014 + type: ARM_RELOC_BR24 + length: 2 + pc-rel: true + extern: true + symbol: 3 + - offset: 0x00000010 + type: ARM_RELOC_BR24 + length: 2 + pc-rel: true + extern: true + symbol: 3 + - offset: 0x00000006 + type: ARM_THUMB_RELOC_BR22 + length: 2 + pc-rel: true + extern: true + symbol: 2 + - offset: 0x00000002 + type: ARM_THUMB_RELOC_BR22 + length: 2 + pc-rel: true + extern: true + symbol: 2 +global-symbols: + - name: _a2 + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x000000000000000C + - name: _t2 + type: N_SECT + scope: [ N_EXT ] + sect: 1 + desc: [ N_ARM_THUMB_DEF ] + value: 0x0000000000000000 +undefined-symbols: + - name: _a1 + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: _t1 + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + +... + + +# CHECK: (('section_name', '__text\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') +# CHECK: ('segment_name', '__TEXT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') +# CHECK: ('_section_data', '00bf00f0 10e800f0 19b80000 00f020e3 000000fa 0f0000ea 00bffff7 f8ef00f0 07b80000 00f020e3 f4fffffa 050000ea dff804c0 ff446047 d4ffffff dff804c0 ff446047 e0ffffff 04c09fe5 0cc08fe0 1cff2fe1 adffffff 04c09fe5 0cc08fe0 1cff2fe1 b5ffffff') + +# When we get a good mach-o disassembler the above __text section content check can be change to be symbolic. + + +# Input file one: +# +# .align 2 +# .code 16 +# .globl _t1 +# .thumb_func _t1 +#_t1: +# nop +# blx _a2 +# b _a2 +# +# .code 32 +# .align 2 +# .globl _a1 +#_a1: +# nop +# blx _t2 +# b _t2 + + + +# Input file two: +# +# .align 2 +# .code 16 +# .globl _t2 +# .thumb_func _t2 +#_t2: +# nop +# blx _a1 +# b _a1 +# +# .code 32 +# .align 2 +# .globl _a2 +#_a2: +# nop +# blx _t1 +# b _t1 diff --git a/test/mach-o/arm-subsections-via-symbols.yaml b/test/mach-o/arm-subsections-via-symbols.yaml new file mode 100644 index 0000000000000..b704568f37b10 --- /dev/null +++ b/test/mach-o/arm-subsections-via-symbols.yaml @@ -0,0 +1,60 @@ +# RUN: lld -flavor darwin -arch armv7 %s -r -print_atoms -o %t | FileCheck %s +# +# Test that assembly written without .subsections_via_symbols is parsed so +# that atoms are non-dead-strip and there is a layout-after references +# chaining atoms together. +# + +--- !mach-o +arch: armv7 +file-type: MH_OBJECT +flags: [ ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 2 + address: 0x0000000000000000 + content: [ 0x04, 0x10, 0x9F, 0xE5, 0x04, 0x20, 0x9F, 0xE5, + 0x1E, 0xFF, 0x2F, 0xE1, 0x78, 0x56, 0x34, 0x12, + 0x21, 0x43, 0x65, 0x87 ] +local-symbols: + - name: constants1 + type: N_SECT + sect: 1 + value: 0x000000000000000C + - name: constants2 + type: N_SECT + sect: 1 + value: 0x0000000000000010 +global-symbols: + - name: _foo + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +... + + +# CHECK:defined-atoms: +# CHECK: - name: _foo +# CHECK: scope: global +# CHECK: content: [ 04, 10, 9F, E5, 04, 20, 9F, E5, 1E, FF, 2F, E1 ] +# CHECK: dead-strip: never +# CHECK: references: +# CHECK: - kind: layout-after +# CHECK: offset: 0 +# CHECK: target: constants1 +# CHECK: - name: constants1 +# CHECK: content: [ 78, 56, 34, 12 ] +# CHECK: dead-strip: never +# CHECK: references: +# CHECK: - kind: layout-after +# CHECK: offset: 0 +# CHECK: target: constants2 +# CHECK: - name: constants2 +# CHECK: content: [ 21, 43, 65, 87 ] +# CHECK: dead-strip: never diff --git a/test/mach-o/cstring-sections.yaml b/test/mach-o/cstring-sections.yaml new file mode 100644 index 0000000000000..940f048b5c6cd --- /dev/null +++ b/test/mach-o/cstring-sections.yaml @@ -0,0 +1,91 @@ +# RUN: lld -flavor darwin -arch x86_64 -r %s -o %t -print_atoms | FileCheck %s +# +# Test -keep_private_externs in -r mode. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __objc_methname + type: S_CSTRING_LITERALS + attributes: [ ] + address: 0x0000000000000000 + content: [ 0x61, 0x62, 0x63, 0x00, 0x64, 0x65, 0x66, 0x00 ] + - segment: __TEXT + section: __objc_classname + type: S_CSTRING_LITERALS + attributes: [ ] + address: 0x0000000000000006 + content: [ 0x61, 0x62, 0x63, 0x00, 0x67, 0x68, 0x69, 0x00 ] + - segment: __TEXT + section: __cstring + type: S_CSTRING_LITERALS + attributes: [ ] + address: 0x000000000000000A + content: [ 0x61, 0x62, 0x63, 0x00, 0x6A, 0x6B, 0x6C, 0x00 ] + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __objc_methname + type: S_CSTRING_LITERALS + attributes: [ ] + address: 0x0000000000000000 + content: [ 0x61, 0x62, 0x63, 0x00 ] + - segment: __TEXT + section: __objc_classname + type: S_CSTRING_LITERALS + attributes: [ ] + address: 0x0000000000000006 + content: [ 0x61, 0x62, 0x63, 0x00 ] + - segment: __TEXT + section: __cstring + type: S_CSTRING_LITERALS + attributes: [ ] + address: 0x000000000000000A + content: [ 0x61, 0x62, 0x63, 0x00 ] + + +... + +# CHECK: defined-atoms: +# CHECK: - scope: hidden +# CHECK: type: c-string +# CHECK: content: [ 61, 62, 63, 00 ] +# CHECK: merge: by-content +# CHECK: section-choice: custom-required +# CHECK: section-name: __TEXT/__objc_methname +# CHECK: - scope: hidden +# CHECK: type: c-string +# CHECK: content: [ 64, 65, 66, 00 ] +# CHECK: merge: by-content +# CHECK: section-choice: custom-required +# CHECK: section-name: __TEXT/__objc_methname +# CHECK: - scope: hidden +# CHECK: type: c-string +# CHECK: content: [ 61, 62, 63, 00 ] +# CHECK: merge: by-content +# CHECK: section-choice: custom-required +# CHECK: section-name: __TEXT/__objc_classname +# CHECK: - scope: hidden +# CHECK: type: c-string +# CHECK: content: [ 67, 68, 69, 00 ] +# CHECK: merge: by-content +# CHECK: section-choice: custom-required +# CHECK: section-name: __TEXT/__objc_classname +# CHECK: - scope: hidden +# CHECK: type: c-string +# CHECK: content: [ 61, 62, 63, 00 ] +# CHECK: merge: by-content +# CHECK: - scope: hidden +# CHECK: type: c-string +# CHECK: content: [ 6A, 6B, 6C, 00 ] +# CHECK: merge: by-content diff --git a/test/mach-o/data-only-dylib.yaml b/test/mach-o/data-only-dylib.yaml new file mode 100644 index 0000000000000..c285066ad778b --- /dev/null +++ b/test/mach-o/data-only-dylib.yaml @@ -0,0 +1,27 @@ +# RUN: lld -flavor darwin -arch x86_64 -dylib %s -o %t %p/Inputs/libSystem.yaml +# RUN: llvm-nm %t | FileCheck %s +# +# Test that a data-only dylib can be built. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __DATA + section: __data + type: S_REGULAR + attributes: [ ] + alignment: 2 + address: 0x0000000000000000 + content: [ 0x00, 0x00, 0x00, 0x00 ] +global-symbols: + - name: _myData + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +... + +# CHECK: _myData diff --git a/test/mach-o/demangle.yaml b/test/mach-o/demangle.yaml new file mode 100644 index 0000000000000..6405458084777 --- /dev/null +++ b/test/mach-o/demangle.yaml @@ -0,0 +1,74 @@ +# REQUIRES: system-linker-mach-o +# +# RUN: not lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s \ +# RUN: -dylib -o %t %p/Inputs/libSystem.yaml 2> %t.err +# RUN: FileCheck %s < %t.err +# +# RUN: not lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s \ +# RUN: -dylib -o %t %p/Inputs/libSystem.yaml -demangle 2> %t.err2 +# RUN: FileCheck %s --check-prefix=DCHECK < %t.err2 +# +# Test -demangle option works on undefined symbol errors. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0xE8, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, + 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x0000000B + type: X86_64_RELOC_BRANCH + length: 2 + pc-rel: true + extern: true + symbol: 2 + - offset: 0x00000006 + type: X86_64_RELOC_BRANCH + length: 2 + pc-rel: true + extern: true + symbol: 3 + - offset: 0x00000001 + type: X86_64_RELOC_BRANCH + length: 2 + pc-rel: true + extern: true + symbol: 1 +global-symbols: + - name: __Z1xv + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: __Znam + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: __Znotcpp + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: _foo + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + +... + +# CHECK: __Znotcpp +# CHECK: __Znam +# CHECK: _foo + +# DCHECK: __Znotcpp +# DCHECK: operator new[](unsigned long) +# DCHECK: _foo + diff --git a/test/mach-o/dependency_info.yaml b/test/mach-o/dependency_info.yaml new file mode 100644 index 0000000000000..dcce4381dfd37 --- /dev/null +++ b/test/mach-o/dependency_info.yaml @@ -0,0 +1,24 @@ +# XFAIL: win32 +# This test fails on Windows because the linker would use '\' instead +# of '/' as a path separator when concatenating path components. +# So the output from the linker would be different. + +# Test -dependency_info option +# +# RUN: lld -flavor darwin -arch x86_64 -test_file_usage \ +# RUN: -dependency_info %t.info \ +# RUN: -path_exists /System/Library/Frameworks \ +# RUN: -path_exists /System/Library/Frameworks/Foo.framework/Foo \ +# RUN: -path_exists /Custom/Frameworks \ +# RUN: -path_exists /Custom/Frameworks/Bar.framework/Bar \ +# RUN: -F/Custom/Frameworks \ +# RUN: -framework Bar \ +# RUN: -framework Foo +# RUN: python %p/Inputs/DependencyDump.py %t.info | FileCheck %s + + +# CHECK: linker-vers: lld +# CHECK: input-file: /Custom/Frameworks/Bar.framework/Bar +# CHECK: not-found: /Custom/Frameworks/Foo.framework/Foo +# CHECK: input-file: /System/Library/Frameworks/Foo.framework/Foo +# CHECK: output-file: a.out diff --git a/test/mach-o/dso_handle.yaml b/test/mach-o/dso_handle.yaml new file mode 100644 index 0000000000000..d39d2c2308064 --- /dev/null +++ b/test/mach-o/dso_handle.yaml @@ -0,0 +1,62 @@ +# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/libSystem.yaml -o %t1 +# RUN: llvm-nm -m -n %t1 | FileCheck %s +# +# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/libSystem.yaml -dead_strip -o %t2 +# RUN: llvm-nm -m -n %t2 | FileCheck %s +# +# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/libSystem.yaml -dylib -o %t3 +# RUN: llvm-nm -m -n %t3 | FileCheck %s +# +# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/libSystem.yaml -bundle -o %t4 +# RUN: llvm-nm -m -n %t4 | FileCheck %s +# +# Test that ___dso_handle symbol is available for executables, bundles, and dylibs +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x31, 0xC0, 0x5D, 0xC3 ] + - segment: __DATA + section: __data + type: S_REGULAR + attributes: [ ] + alignment: 3 + address: 0x0000000000000008 + content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000000 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 2 +global-symbols: + - name: _d + type: N_SECT + scope: [ N_EXT ] + sect: 2 + value: 0x0000000000000008 + - name: _main + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: ___dso_handle + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + + +... + +# CHECK_NOT: ___dso_handle +# CHECK: _main diff --git a/test/mach-o/dylib-exports.yaml b/test/mach-o/dylib-exports.yaml new file mode 100644 index 0000000000000..0a00dfe1e9526 --- /dev/null +++ b/test/mach-o/dylib-exports.yaml @@ -0,0 +1,41 @@ +# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 -dylib \ +# RUN: %s %p/Inputs/libSystem.yaml -o %t && \ +# RUN: llvm-objdump -exports-trie %t | FileCheck %s +# +# +# Tests that exports trie builds properly. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0xC3, 0xC3, 0xC3 ] +global-symbols: + - name: _myHidden + type: N_SECT + scope: [ N_EXT, N_PEXT ] + sect: 1 + value: 0x0000000000000000 + - name: _myRegular + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000001 + - name: _myWeak + type: N_SECT + scope: [ N_EXT ] + sect: 1 + desc: [ N_WEAK_DEF ] + value: 0x0000000000000002 +... + +# CHECK-NOT: _myHidden +# CHECK: _myRegular +# CHECK: _myWeak [weak_def] diff --git a/test/mach-o/dylib-install-names.yaml b/test/mach-o/dylib-install-names.yaml new file mode 100644 index 0000000000000..845085be42aba --- /dev/null +++ b/test/mach-o/dylib-install-names.yaml @@ -0,0 +1,74 @@ +# Check we accept -install_name correctly: +# RUN: lld -flavor darwin -arch x86_64 -install_name libwibble.dylib -dylib \ +# RUN: -compatibility_version 2.0 -current_version 5.3 \ +# RUN: %p/Inputs/libSystem.yaml %s -o %t.dylib +# RUN: llvm-objdump -private-headers %t.dylib | FileCheck %s --check-prefix=CHECK-BINARY-WRITE + +# Check we read LC_ID_DYLIB correctly: +# RUN: lld -flavor darwin -arch x86_64 %p/Inputs/use-dylib-install-names.yaml \ +# RUN: %p/Inputs/libSystem.yaml %t.dylib -dylib -o %t2.dylib +# RUN: llvm-objdump -private-headers %t2.dylib | FileCheck %s --check-prefix=CHECK-BINARY-READ + +# Check we default the install-name to the output file: +# RUN: lld -flavor darwin -arch x86_64 -dylib %s -o libwibble.dylib \ +# RUN: -compatibility_version 2.0 -current_version 5.3 \ +# RUN: %p/Inputs/libSystem.yaml +# RUN: llvm-objdump -private-headers libwibble.dylib | FileCheck %s --check-prefix=CHECK-BINARY-WRITE +# RUN: rm -f libwibble.dylib + +# Check -single_module does nothing +# RUN: lld -flavor darwin -arch x86_64 -dylib %s -install_name libwibble.dylib \ +# RUN: -compatibility_version 2.0 -current_version 5.3 \ +# RUN: -single_module -o %t2.dylib %p/Inputs/libSystem.yaml +# RUN: llvm-objdump -private-headers %t2.dylib | FileCheck %s --check-prefix=CHECK-BINARY-WRITE + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 4 + address: 0x0000000000000000 + content: [ 0xCC, 0xC3, 0x90, 0xC3, 0x90, 0x90, 0xC3, 0x90, + 0x90, 0x90, 0xC3, 0x90, 0x90, 0x90, 0x90, 0xC3, + 0x31, 0xC0, 0xC3 ] +local-symbols: + - name: _myStatic + type: N_SECT + sect: 1 + value: 0x000000000000000B +global-symbols: + - name: _myGlobal + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000001 +... + + +# CHECK-BINARY-WRITE: cmd LC_ID_DYLIB +# CHECK-BINARY-WRITE-NEXT: cmdsize 40 +# CHECK-BINARY-WRITE-NEXT: name libwibble.dylib (offset 24) +# CHECK-BINARY-WRITE-NEXT: time stamp 1 +# CHECK-BINARY-WRITE-NEXT: current version 5.3.0 +# CHECK-BINARY-WRITE-NEXT: compatibility version 2.0.0 + +# CHECK-BINARY-READ: cmd LC_LOAD_DYLIB +# CHECK-BINARY-READ-NEXT: cmdsize 56 +# CHECK-BINARY-READ-NEXT: name /usr/lib/libSystem.B.dylib (offset 24) +# CHECK-BINARY-READ-NEXT: time stamp 2 +# CHECK-BINARY-READ-NEXT: current version 0.16.0 +# CHECK-BINARY-READ-NEXT: compatibility version 0.16.0 + +# CHECK-BINARY-READ: cmd LC_LOAD_DYLIB +# CHECK-BINARY-READ-NEXT: cmdsize 40 +# CHECK-BINARY-READ-NEXT: name libwibble.dylib (offset 24) +# CHECK-BINARY-READ-NEXT: time stamp 2 +# CHECK-BINARY-READ-NEXT: current version 5.3.0 +# CHECK-BINARY-READ-NEXT: compatibility version 2.0.0 diff --git a/test/mach-o/exe-offsets.yaml b/test/mach-o/exe-offsets.yaml new file mode 100644 index 0000000000000..a751507432eef --- /dev/null +++ b/test/mach-o/exe-offsets.yaml @@ -0,0 +1,45 @@ +# RUN: lld -flavor darwin -arch x86_64 %s -o %t -e start %p/Inputs/libSystem.yaml +# RUN: llvm-readobj -sections %t | FileCheck %s + +# Make sure data gets put at offset + +--- !native +defined-atoms: + - name: start + scope: global + content: [ 90 ] + + - name: _s1 + type: data + content: [ 31, 32, 33, 34 ] + + - name: _s2 + type: zero-fill + size: 8192 + + - name: _s3 + type: zero-fill + size: 100 + + - name: _s4 + type: data + content: [ 01 ] + + +# CHECK-LABEL: Section { +# CHECK: Name: __text +# CHECK: Segment: __TEXT +# CHECK: Size: 0x1 +# CHECK: Offset: 0 + +# CHECK-LABEL: Section { +# CHECK: Name: __data +# CHECK: Segment: __DATA +# CHECK: Size: 0x5 +# CHECK: Offset: 4096 + +# CHECK-LABEL: Section { +# CHECK: Name: __bss +# CHECK: Segment: __DATA +# CHECK: Size: 0x2064 +# CHECK: Offset: 0 diff --git a/test/mach-o/exe-segment-overlap.yaml b/test/mach-o/exe-segment-overlap.yaml new file mode 100644 index 0000000000000..a416ee3ca73b2 --- /dev/null +++ b/test/mach-o/exe-segment-overlap.yaml @@ -0,0 +1,44 @@ +# RUN: lld -flavor darwin -arch x86_64 %s -o %t %p/Inputs/libSystem.yaml +# RUN: llvm-readobj -sections -section-data %t | FileCheck %s + +--- !native +defined-atoms: + - name: _main + scope: global + content: [ 90 ] + + - name: _s2 + type: data + content: [ 31, 32, 33, 34 ] + + - name: _kustom + scope: global + type: unknown + content: [ 01, 02, 03, 04, 05, 06, 07, 08 ] + section-choice: custom-required + section-name: __CUST/__custom + + +# CHECK-LABEL: Section { +# CHECK: Name: __text +# CHECK: Segment: __TEXT +# CHECK: Size: 0x1 +# CHECK: Offset: 4095 + +# CHECK-LABEL: Section { +# CHECK: Name: __data +# CHECK: Segment: __DATA +# CHECK: Size: 0x4 +# CHECK: Offset: 4096 +# CHECK: SectionData ( +# CHECK-NEXT: 0000: 31323334 +# CHECK-NEXT: ) + +# CHECK-LABEL: Section { +# CHECK: Name: __custom{{ }} +# CHECK: Segment: __CUST{{ }} +# CHECK: Size: 0x8 +# CHECK: Offset: 8192 +# CHECK: SectionData ( +# CHECK-NEXT: 0000: 01020304 05060708 +# CHECK-NEXT: ) diff --git a/test/mach-o/exported_symbols_list-dylib.yaml b/test/mach-o/exported_symbols_list-dylib.yaml new file mode 100644 index 0000000000000..71121d7400f6a --- /dev/null +++ b/test/mach-o/exported_symbols_list-dylib.yaml @@ -0,0 +1,77 @@ +# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 -dylib \ +# RUN: %s %p/Inputs/libSystem.yaml -o %t \ +# RUN: -exported_symbols_list %p/Inputs/exported_symbols_list.exp && \ +# RUN: llvm-nm -m %t | FileCheck %s +# +# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 -dylib \ +# RUN: %s %p/Inputs/libSystem.yaml -o %t2 \ +# RUN: -exported_symbol _foo -exported_symbol _b && \ +# RUN: llvm-nm -m %t2 | FileCheck %s +# +# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 -dylib \ +# RUN: %s %p/Inputs/libSystem.yaml -o %t3 \ +# RUN: -unexported_symbol _bar -unexported_symbol _a && \ +# RUN: llvm-nm -m %t3 | FileCheck %s +# +# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 -dylib \ +# RUN: %s %p/Inputs/libSystem.yaml -dead_strip -o %t \ +# RUN: -exported_symbols_list %p/Inputs/exported_symbols_list.exp && \ +# RUN: llvm-nm -m %t | FileCheck -check-prefix=CHECK_DEAD %s +# +# Test -exported_symbols_list and -exported_symbol properly changes visibility. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x5D, 0xC3, 0x55, 0x48, + 0x89, 0xE5, 0x5D, 0xC3 ] + - segment: __DATA + section: __data + type: S_REGULAR + attributes: [ ] + alignment: 2 + address: 0x000000000000000C + content: [ 0x0A, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00 ] + +global-symbols: + - name: _a + type: N_SECT + scope: [ N_EXT ] + sect: 2 + value: 0x000000000000000C + - name: _b + type: N_SECT + scope: [ N_EXT ] + sect: 2 + value: 0x0000000000000010 + - name: _bar + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000006 + - name: _foo + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + + +... + +# CHECK: (__DATA,__data) non-external (was a private external) _a +# CHECK: (__DATA,__data) external _b +# CHECK: (__TEXT,__text) non-external (was a private external) _bar +# CHECK: (__TEXT,__text) external _foo + +# CHECK_DEAD-NOT: (__DATA,__data) non-external (was a private external) _a +# CHECK_DEAD: (__DATA,__data) external _b +# CHECK_DEAD-NOT: (__TEXT,__text) non-external (was a private external) _bar +# CHECK_DEAD: (__TEXT,__text) external _foo diff --git a/test/mach-o/exported_symbols_list-obj.yaml b/test/mach-o/exported_symbols_list-obj.yaml new file mode 100644 index 0000000000000..735162dfedc88 --- /dev/null +++ b/test/mach-o/exported_symbols_list-obj.yaml @@ -0,0 +1,67 @@ +# RUN: lld -flavor darwin -arch x86_64 -r %s -o %t -exported_symbol _bar \ +# RUN: && llvm-nm -m %t | FileCheck %s +# +# RUN: lld -flavor darwin -arch x86_64 -r %s -o %t2 -keep_private_externs \ +# RUN: -exported_symbol _bar && \ +# RUN: llvm-nm -m %t2 | FileCheck -check-prefix=CHECK_KPE %s +# +# RUN: not lld -flavor darwin -arch x86_64 -r %s -o %t3 \ +# RUN: -exported_symbol _foo 2> %t4 + +# Test -exported_symbols_list properly changes visibility in -r mode. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x5D, 0xC3, 0x55, 0x48, + 0x89, 0xE5, 0x5D, 0xC3 ] + - segment: __DATA + section: __data + type: S_REGULAR + attributes: [ ] + alignment: 2 + address: 0x000000000000000C + content: [ 0x0A, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00 ] + +global-symbols: + - name: _a + type: N_SECT + scope: [ N_EXT ] + sect: 2 + value: 0x000000000000000C + - name: _b + type: N_SECT + scope: [ N_EXT, N_PEXT ] + sect: 2 + value: 0x0000000000000010 + - name: _bar + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000006 + - name: _foo + type: N_SECT + scope: [ N_EXT, N_PEXT ] + sect: 1 + value: 0x0000000000000000 + + +... + +# CHECK: (__DATA,__data) non-external (was a private external) _a +# CHECK: (__DATA,__data) non-external (was a private external) _b +# CHECK: (__TEXT,__text) external _bar +# CHECK: (__TEXT,__text) non-external (was a private external) _foo + +# CHECK_KPE: (__DATA,__data) non-external (was a private external) _a +# CHECK_KPE: (__DATA,__data) private external _b +# CHECK_KPE: (__TEXT,__text) external _bar +# CHECK_KPE: (__TEXT,__text) private external _foo diff --git a/test/mach-o/exported_symbols_list-undef.yaml b/test/mach-o/exported_symbols_list-undef.yaml new file mode 100644 index 0000000000000..c6a8d8f4022f6 --- /dev/null +++ b/test/mach-o/exported_symbols_list-undef.yaml @@ -0,0 +1,55 @@ +# RUN: not lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 -dylib \ +# RUN: %s %p/Inputs/libSystem.yaml -o %t -exported_symbol _foobar 2> %t2 +# +# Test -exported_symbol fails if exported symbol not found. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x5D, 0xC3, 0x55, 0x48, + 0x89, 0xE5, 0x5D, 0xC3 ] + - segment: __DATA + section: __data + type: S_REGULAR + attributes: [ ] + alignment: 2 + address: 0x000000000000000C + content: [ 0x0A, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00 ] + +global-symbols: + - name: _a + type: N_SECT + scope: [ N_EXT ] + sect: 2 + value: 0x000000000000000C + - name: _b + type: N_SECT + scope: [ N_EXT ] + sect: 2 + value: 0x0000000000000010 + - name: _bar + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000006 + - name: _foo + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + + +... + +# CHECK: (__DATA,__data) private external _a +# CHECK: (__DATA,__data) external _b +# CHECK: (__TEXT,__text) private external _bar +# CHECK: (__TEXT,__text) external _foo diff --git a/test/mach-o/fat-archive.yaml b/test/mach-o/fat-archive.yaml new file mode 100644 index 0000000000000..5db7fd96ff0e8 --- /dev/null +++ b/test/mach-o/fat-archive.yaml @@ -0,0 +1,45 @@ +# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t \ +# RUN: -L %p/Inputs -lfoo %p/Inputs/libSystem.yaml +# RUN: llvm-nm -m -n %t | FileCheck %s +# +# Test that fat archives are handled. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 4 + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x83, 0xEC, 0x10, + 0xC7, 0x45, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xB0, + 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x31, 0xC0, + 0x48, 0x83, 0xC4, 0x10, 0x5D, 0xC3 ] + relocations: + - offset: 0x00000012 + type: X86_64_RELOC_BRANCH + length: 2 + pc-rel: true + extern: true + symbol: 1 +global-symbols: + - name: _main + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: _foo + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + +... + +# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _main +# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _foo diff --git a/test/mach-o/filelist.yaml b/test/mach-o/filelist.yaml new file mode 100644 index 0000000000000..28bfeb74d02bd --- /dev/null +++ b/test/mach-o/filelist.yaml @@ -0,0 +1,18 @@ +# RUN: lld -flavor darwin -test_file_usage \ +# RUN: -filelist %p/Inputs/full.filelist \ +# RUN: -path_exists /foo/bar/a.o \ +# RUN: -path_exists /foo/bar/b.o \ +# RUN: -path_exists /foo/x.a \ +# RUN: 2>&1 | FileCheck %s +# +# RUN: lld -flavor darwin -test_file_usage -t \ +# RUN: -filelist %p/Inputs/partial.filelist,/foo \ +# RUN: -path_exists /foo/bar/a.o \ +# RUN: -path_exists /foo/bar/b.o \ +# RUN: -path_exists /foo/x.a \ +# RUN: 2>&1 | FileCheck %s + + +# CHECK: Found filelist entry /foo/bar/a.o +# CHECK: Found filelist entry /foo/bar/b.o +# CHECK: Found filelist entry /foo/x.a diff --git a/test/mach-o/force_load-dylib.yaml b/test/mach-o/force_load-dylib.yaml new file mode 100644 index 0000000000000..0b932e1598824 --- /dev/null +++ b/test/mach-o/force_load-dylib.yaml @@ -0,0 +1,45 @@ +# RUN: lld -flavor darwin -arch x86_64 -dylib %p/Inputs/bar.yaml \ +# RUN: -install_name /usr/lib/libbar.dylib %p/Inputs/libSystem.yaml -o %t1.dylib +# RUN: lld -flavor darwin -arch x86_64 -dylib %s -all_load %t1.dylib \ +# RUN: -install_name /usr/lib/libfoo.dylib %p/Inputs/libSystem.yaml -o %t +# RUN: llvm-nm -m %t | FileCheck %s +# +# +# Test -all_load does not break linking with dylibs +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x31, 0xC0, 0x5D, 0xE9, + 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000008 + type: X86_64_RELOC_BRANCH + length: 2 + pc-rel: true + extern: true + symbol: 1 +global-symbols: + - name: _foo + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: _bar + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + +... + + +# CHECK: (__TEXT,__text) external _foo diff --git a/test/mach-o/force_load-x86_64.yaml b/test/mach-o/force_load-x86_64.yaml new file mode 100644 index 0000000000000..35905effd2c4d --- /dev/null +++ b/test/mach-o/force_load-x86_64.yaml @@ -0,0 +1,38 @@ +# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/libSystem.yaml \ +# RUN: %p/Inputs/libfoo.a %p/Inputs/libbar.a -o %t1 +# RUN: llvm-nm -m -n %t1 | FileCheck %s +# +# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/libSystem.yaml \ +# RUN: -force_load %p/Inputs/libfoo.a %p/Inputs/libbar.a -o %t2 +# RUN: llvm-nm -m -n %t2 | FileCheck --check-prefix=CHECKF %s +# +# Test that -force_load causes members of static library to be loaded. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0xC3 ] +global-symbols: + - name: _main + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +... + +# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _main +# CHECK-NOT: {{[0-9a-f]+}} (__TEXT,__text) external _main + +# CHECKF: {{[0-9a-f]+}} (__TEXT,__text) external _main +# CHECKF: {{[0-9a-f]+}} (__TEXT,__text) external _foo +# CHECKF-NOT: {{[0-9a-f]+}} (__TEXT,__text) external _bar diff --git a/test/mach-o/framework-user-paths.yaml b/test/mach-o/framework-user-paths.yaml new file mode 100644 index 0000000000000..f6ac64779a717 --- /dev/null +++ b/test/mach-o/framework-user-paths.yaml @@ -0,0 +1,41 @@ +# +# Test framework and SDK search paths. +# myFrameworks is not an absolute path, so it should not by found in SDK +# /Custom/Frameworks should be found in SDK +# /opt/Frameworks should not be found in SDK +# /System/Library/Frameworks is implicit and should be in SDK +# +# RUN: lld -flavor darwin -arch x86_64 -r -test_file_usage -v \ +# RUN: -path_exists myFrameworks \ +# RUN: -path_exists myFrameworks/my.framework/my \ +# RUN: -path_exists /opt/Frameworks \ +# RUN: -path_exists /opt/Frameworks/other.framework/other \ +# RUN: -path_exists /Custom/Frameworks \ +# RUN: -path_exists /Custom/Frameworks/Bar.framework/Bar \ +# RUN: -path_exists /System/Library/Frameworks \ +# RUN: -path_exists /System/Library/Frameworks/Foo.framework/Foo \ +# RUN: -path_exists /SDK/myFrameworks \ +# RUN: -path_exists /SDK/myFrameworks/my.framework/my \ +# RUN: -path_exists /SDK/Custom/Frameworks \ +# RUN: -path_exists /SDK/Custom/Frameworks/Bar.framework/Bar \ +# RUN: -path_exists /SDK/System/Library/Frameworks \ +# RUN: -path_exists /SDK/System/Library/Frameworks/Foo.framework/Foo \ +# RUN: -syslibroot /SDK \ +# RUN: -FmyFrameworks \ +# RUN: -F/Custom/Frameworks \ +# RUN: -F/opt/Frameworks \ +# RUN: -framework my \ +# RUN: -framework Bar \ +# RUN: -framework Foo \ +# RUN: -framework other \ +# RUN: 2>&1 | FileCheck %s + +# CHECK: Framework search paths: +# CHECK-NEXT: myFrameworks +# CHECK-NEXT: /SDK/Custom/Frameworks +# CHECK-NEXT: /opt/Frameworks +# CHECK-NEXT: /SDK/System/Library/Frameworks +# CHECK: Found framework myFrameworks/my.framework/my +# CHECK: Found framework /SDK/Custom/Frameworks/Bar.framework/Bar +# CHECK: Found framework /SDK/System/Library/Frameworks/Foo.framework/Foo +# CHECK: Found framework /opt/Frameworks/other.framework/other diff --git a/test/mach-o/got-order.yaml b/test/mach-o/got-order.yaml new file mode 100644 index 0000000000000..fbbc4e0397f21 --- /dev/null +++ b/test/mach-o/got-order.yaml @@ -0,0 +1,134 @@ +# RUN: lld -flavor darwin -arch x86_64 %s -o %t %p/Inputs/libSystem.yaml +# RUN: llvm-objdump -bind %t | FileCheck %s +# +# Test that GOT slots are sorted by name +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x8B, 0x0D, 0x00, + 0x00, 0x00, 0x00, 0x48, 0x8B, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x8B, 0x00, 0x03, 0x01, 0x48, 0x8B, + 0x0D, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x5D, + 0xC3 ] + relocations: + - offset: 0x00000019 + type: X86_64_RELOC_GOT_LOAD + length: 2 + pc-rel: true + extern: true + symbol: 2 + - offset: 0x0000000E + type: X86_64_RELOC_GOT_LOAD + length: 2 + pc-rel: true + extern: true + symbol: 1 + - offset: 0x00000007 + type: X86_64_RELOC_GOT_LOAD + length: 2 + pc-rel: true + extern: true + symbol: 3 +global-symbols: + - name: _func + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: _aaa + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: _fff + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: _zzz + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x8B, 0x0D, 0x00, + 0x00, 0x00, 0x00, 0x48, 0x8B, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x8B, 0x00, 0x03, 0x01, 0x48, 0x8B, + 0x0D, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x5D, + 0xC3 ] + relocations: + - offset: 0x00000019 + type: X86_64_RELOC_GOT_LOAD + length: 2 + pc-rel: true + extern: true + symbol: 2 + - offset: 0x0000000E + type: X86_64_RELOC_GOT_LOAD + length: 2 + pc-rel: true + extern: true + symbol: 1 + - offset: 0x00000007 + type: X86_64_RELOC_GOT_LOAD + length: 2 + pc-rel: true + extern: true + symbol: 3 +global-symbols: + - name: _main + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: _bar + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: _foo + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: _zazzle + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + +--- !mach-o +arch: x86_64 +file-type: MH_DYLIB +install-name: /usr/lib/libfoobar.dylib +exports: + - name: _bar + - name: _zazzle + - name: _foo + - name: _aaa + - name: _fff + - name: _zzz +... + + +# CHECK: __DATA __got {{[0-9a-zA-Z _]+}} pointer 0 libfoobar _aaa +# CHECK-NEXT: __DATA __got {{[0-9a-zA-Z _]+}} pointer 0 libfoobar _bar +# CHECK-NEXT: __DATA __got {{[0-9a-zA-Z _]+}} pointer 0 libfoobar _fff +# CHECK-NEXT: __DATA __got {{[0-9a-zA-Z _]+}} pointer 0 libfoobar _foo +# CHECK-NEXT: __DATA __got {{[0-9a-zA-Z _]+}} pointer 0 libfoobar _zazzle +# CHECK-NEXT: __DATA __got {{[0-9a-zA-Z _]+}} pointer 0 libfoobar _zzz diff --git a/test/mach-o/hello-world-arm64.yaml b/test/mach-o/hello-world-arm64.yaml new file mode 100644 index 0000000000000..a0555e9cc426b --- /dev/null +++ b/test/mach-o/hello-world-arm64.yaml @@ -0,0 +1,104 @@ +# RUN: lld -flavor darwin -arch arm64 %s -o %t +# RUN: llvm-nm -m -n %t | FileCheck %s +# +# Test that arm64 hello-world can be linked into a mach-o executable +# + +--- !mach-o +arch: arm64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 2 + address: 0x0000000000000000 + content: [ 0xFD, 0x7B, 0xBF, 0xA9, 0xFD, 0x03, 0x00, 0x91, + 0x08, 0x00, 0x00, 0x90, 0x08, 0x01, 0x40, 0xF9, + 0x00, 0x01, 0x40, 0xF9, 0x01, 0x00, 0x00, 0x90, + 0x21, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x94, + 0x00, 0x00, 0x80, 0x52, 0xFD, 0x7B, 0xC1, 0xA8, + 0xC0, 0x03, 0x5F, 0xD6 ] + relocations: + - offset: 0x0000001C + type: ARM64_RELOC_BRANCH26 + length: 2 + pc-rel: true + extern: true + symbol: 5 + - offset: 0x00000018 + type: ARM64_RELOC_PAGEOFF12 + length: 2 + pc-rel: false + extern: true + symbol: 1 + - offset: 0x00000014 + type: ARM64_RELOC_PAGE21 + length: 2 + pc-rel: true + extern: true + symbol: 1 + - offset: 0x0000000C + type: ARM64_RELOC_GOT_LOAD_PAGEOFF12 + length: 2 + pc-rel: false + extern: true + symbol: 4 + - offset: 0x00000008 + type: ARM64_RELOC_GOT_LOAD_PAGE21 + length: 2 + pc-rel: true + extern: true + symbol: 4 + - segment: __TEXT + section: __cstring + type: S_CSTRING_LITERALS + attributes: [ ] + address: 0x000000000000002C + content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00 ] +local-symbols: + - name: ltmp0 + type: N_SECT + sect: 1 + value: 0x0000000000000000 + - name: l_.str + type: N_SECT + sect: 2 + value: 0x000000000000002C + - name: ltmp1 + type: N_SECT + sect: 2 + value: 0x000000000000002C +global-symbols: + - name: _main + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: ___stdoutp + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: _fprintf + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 +... + +--- !mach-o +arch: arm64 +file-type: MH_DYLIB +install-name: /usr/lib/libSystem.B.dylib +exports: + - name: _fprintf + - name: ___stdoutp + - name: dyld_stub_binder +... + +# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _main +# CHECK: (undefined) external ___stdoutp (from libSystem) +# CHECK: (undefined) external _fprintf (from libSystem) +# CHECK: (undefined) external dyld_stub_binder (from libSystem) diff --git a/test/mach-o/hello-world-armv6.yaml b/test/mach-o/hello-world-armv6.yaml new file mode 100644 index 0000000000000..746ee094da68f --- /dev/null +++ b/test/mach-o/hello-world-armv6.yaml @@ -0,0 +1,72 @@ +# RUN: lld -flavor darwin -arch armv6 %s -o %t +# RUN: llvm-nm -m %t | FileCheck %s +# +# Test that armv6 (arm) hello-world can be linked into a mach-o executable +# + +--- !mach-o +arch: armv6 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 2 + address: 0x0000000000000000 + content: [ 0x80, 0x40, 0x2D, 0xE9, 0x10, 0x00, 0x9F, 0xE5, + 0x0D, 0x70, 0xA0, 0xE1, 0x00, 0x00, 0x8F, 0xE0, + 0xFA, 0xFF, 0xFF, 0xEB, 0x00, 0x00, 0xA0, 0xE3, + 0x80, 0x80, 0xBD, 0xE8, 0x0C, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x0000001C + scattered: true + type: ARM_RELOC_SECTDIFF + length: 2 + pc-rel: false + value: 0x00000020 + - offset: 0x00000000 + scattered: true + type: ARM_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x0000000C + - offset: 0x00000010 + type: ARM_RELOC_BR24 + length: 2 + pc-rel: true + extern: true + symbol: 1 + - segment: __TEXT + section: __cstring + type: S_CSTRING_LITERALS + attributes: [ ] + address: 0x0000000000000020 + content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00 ] +global-symbols: + - name: _main + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: _printf + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + +--- !mach-o +arch: armv6 +file-type: MH_DYLIB +install-name: /usr/lib/libSystem.B.dylib +exports: + - name: _printf + - name: dyld_stub_binder +... + +# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _main +# CHECK: (undefined) external _printf (from libSystem) +# CHECK: (undefined) external dyld_stub_binder (from libSystem) diff --git a/test/mach-o/hello-world-armv7.yaml b/test/mach-o/hello-world-armv7.yaml new file mode 100644 index 0000000000000..bfc03c382a05a --- /dev/null +++ b/test/mach-o/hello-world-armv7.yaml @@ -0,0 +1,85 @@ +# RUN: lld -flavor darwin -arch armv7 %s -o %t +# RUN: llvm-nm -m -n %t | FileCheck %s +# +# Test that armv7 (thumb) hello-world can be linked into a mach-o executable +# + +--- !mach-o +arch: armv7 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 2 + address: 0x0000000000000000 + content: [ 0x80, 0xB5, 0x40, 0xF2, 0x06, 0x00, 0x6F, 0x46, + 0xC0, 0xF2, 0x00, 0x00, 0x78, 0x44, 0xFF, 0xF7, + 0xF8, 0xEF, 0x00, 0x20, 0x80, 0xBD ] + relocations: + - offset: 0x0000000E + type: ARM_THUMB_RELOC_BR22 + length: 2 + pc-rel: true + extern: true + symbol: 1 + - offset: 0x00000008 + scattered: true + type: ARM_RELOC_HALF_SECTDIFF + length: 3 + pc-rel: false + value: 0x00000016 + - offset: 0x00000006 + scattered: true + type: ARM_RELOC_PAIR + length: 3 + pc-rel: false + value: 0x0000000C + - offset: 0x00000002 + scattered: true + type: ARM_RELOC_HALF_SECTDIFF + length: 2 + pc-rel: false + value: 0x00000016 + - offset: 0x00000000 + scattered: true + type: ARM_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x0000000C + - segment: __TEXT + section: __cstring + type: S_CSTRING_LITERALS + attributes: [ ] + address: 0x0000000000000016 + content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00 ] +global-symbols: + - name: _main + type: N_SECT + scope: [ N_EXT ] + sect: 1 + desc: [ N_ARM_THUMB_DEF ] + value: 0x0000000000000000 +undefined-symbols: + - name: _printf + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 +... + +--- !mach-o +arch: armv7 +file-type: MH_DYLIB +install-name: /usr/lib/libSystem.B.dylib +exports: + - name: _printf + - name: dyld_stub_binder +... + +# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external [Thumb] _main +# CHECK: (undefined) external _printf (from libSystem) +# CHECK: (undefined) external dyld_stub_binder (from libSystem) diff --git a/test/mach-o/hello-world-x86.yaml b/test/mach-o/hello-world-x86.yaml new file mode 100644 index 0000000000000..de453ed1ac45f --- /dev/null +++ b/test/mach-o/hello-world-x86.yaml @@ -0,0 +1,71 @@ +# RUN: lld -flavor darwin -arch i386 %s -o %t +# RUN: llvm-nm -m %t | FileCheck %s +# +# Test that i386 hello-world can be linked into a mach-o executable +# + +--- !mach-o +arch: x86 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x89, 0xE5, 0x83, 0xEC, 0x08, 0xE8, 0x00, + 0x00, 0x00, 0x00, 0x58, 0x8D, 0x80, 0x16, 0x00, + 0x00, 0x00, 0x89, 0x04, 0x24, 0xE8, 0xE6, 0xFF, + 0xFF, 0xFF, 0x31, 0xC0, 0x83, 0xC4, 0x08, 0x5D, + 0xC3 ] + relocations: + - offset: 0x00000016 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: true + extern: true + symbol: 1 + - offset: 0x0000000E + scattered: true + type: GENERIC_RELOC_LOCAL_SECTDIFF + length: 2 + pc-rel: false + value: 0x00000021 + - offset: 0x00000000 + scattered: true + type: GENERIC_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x0000000B + - segment: __TEXT + section: __cstring + type: S_CSTRING_LITERALS + attributes: [ ] + address: 0x0000000000000021 + content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00 ] +global-symbols: + - name: _main + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: _printf + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + +--- !mach-o +arch: x86 +file-type: MH_DYLIB +install-name: /usr/lib/libSystem.B.dylib +exports: + - name: _printf + - name: dyld_stub_binder + +... + +# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _main +# CHECK: (undefined) external _printf (from libSystem) +# CHECK: (undefined) external dyld_stub_binder (from libSystem) diff --git a/test/mach-o/hello-world-x86_64.yaml b/test/mach-o/hello-world-x86_64.yaml new file mode 100644 index 0000000000000..83d4fcb326012 --- /dev/null +++ b/test/mach-o/hello-world-x86_64.yaml @@ -0,0 +1,126 @@ +# RUN: lld -flavor darwin -arch x86_64 %s -o %t +# RUN: llvm-nm -m -n %t | FileCheck %s +# +# RUN: lld -flavor darwin -arch x86_64 %s -dead_strip -o %t2 +# RUN: llvm-nm -m -n %t2 | FileCheck %s +# +# Test that x86_64 hello-world can be linked into a mach-o executable +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x8B, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x48, 0x8B, 0x38, 0x48, 0x8D, + 0x35, 0x00, 0x00, 0x00, 0x00, 0x31, 0xC0, 0xE8, + 0x00, 0x00, 0x00, 0x00, 0x31, 0xC0, 0x5D, 0xC3 ] + relocations: + - offset: 0x00000018 + type: X86_64_RELOC_BRANCH + length: 2 + pc-rel: true + extern: true + symbol: 5 + - offset: 0x00000011 + type: X86_64_RELOC_SIGNED + length: 2 + pc-rel: true + extern: true + symbol: 0 + - offset: 0x00000007 + type: X86_64_RELOC_GOT_LOAD + length: 2 + pc-rel: true + extern: true + symbol: 4 + - segment: __TEXT + section: __cstring + type: S_CSTRING_LITERALS + attributes: [ ] + address: 0x0000000000000020 + content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00 ] + - segment: __LD + section: __compact_unwind + type: S_REGULAR + attributes: [ ] + alignment: 3 + address: 0x0000000000000028 + content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000000 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: false + symbol: 1 + - segment: __TEXT + section: __eh_frame + type: S_COALESCED + attributes: [ ] + alignment: 3 + address: 0x0000000000000048 + content: [ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x7A, 0x52, 0x00, 0x01, 0x78, 0x10, 0x01, + 0x10, 0x0C, 0x07, 0x08, 0x90, 0x01, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x98, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x41, 0x0E, 0x10, 0x86, 0x02, 0x43, 0x0D, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] +local-symbols: + - name: L1 + type: N_SECT + sect: 2 + value: 0x0000000000000020 + - name: EH_frame0 + type: N_SECT + sect: 4 + value: 0x0000000000000048 +global-symbols: + - name: _main + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + - name: _main.eh + type: N_SECT + scope: [ N_EXT ] + sect: 4 + value: 0x0000000000000060 +undefined-symbols: + - name: ___stdoutp + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: _fprintf + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + +--- !mach-o +arch: x86_64 +file-type: MH_DYLIB +install-name: /usr/lib/libSystem.B.dylib +exports: + - name: _fprintf + - name: dyld_stub_binder + - name: ___stdoutp + +... + +# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _main +# CHECK: (undefined) external ___stdoutp (from libSystem) +# CHECK: (undefined) external _fprintf (from libSystem) +# CHECK: (undefined) external dyld_stub_binder (from libSystem) diff --git a/test/mach-o/image-base.yaml b/test/mach-o/image-base.yaml new file mode 100644 index 0000000000000..cc272491bc55b --- /dev/null +++ b/test/mach-o/image-base.yaml @@ -0,0 +1,27 @@ +# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.9 %s -o %t -image_base 31415926000 %p/Inputs/libSystem.yaml +# RUN: macho-dump %t | FileCheck %s +# RUN: not lld -flavor darwin -arch x86_64 -image_base 0x31415926530 %s >/dev/null 2> %t +# RUN: FileCheck < %t %s --check-prefix=CHECK-ERROR-MISPAGED +# RUN: not lld -flavor darwin -arch x86_64 -image_base 1000 %s >/dev/null 2> %t +# RUN: FileCheck < %t %s --check-prefix=CHECK-ERROR-OVERLAP +# RUN: not lld -flavor darwin -arch x86_64 -image_base hithere %s >/dev/null 2> %t +# RUN: FileCheck < %t %s --check-prefix=CHECK-ERROR-NOTHEX + +--- !native +defined-atoms: + - name: _main + scope: global + content: [] + + # Unfortunately, llvm-objdump and llvm-readobj are too generic and don't give + # us easy access to the MachO segment model, so we have to check the uglier + # macho-dump output. +# CHECK: 'segment_name', '__TEXT +# CHECK-NEXT: 'vm_addr', 3384796143616 + + +# CHECK-ERROR-MISPAGED: error: image_base must be a multiple of page size (0x1000) + +# CHECK-ERROR-OVERLAP: error: image_base overlaps with __PAGEZERO + +# CHECK-ERROR-NOTHEX: error: image_base expects a hex number diff --git a/test/mach-o/infer-arch.yaml b/test/mach-o/infer-arch.yaml new file mode 100644 index 0000000000000..94f8543bd72e3 --- /dev/null +++ b/test/mach-o/infer-arch.yaml @@ -0,0 +1,29 @@ +# RUN: lld -flavor darwin -arch i386 -macosx_version_min 10.8 %s -r -o %t \ +# RUN: && lld -flavor darwin -r %t -o %t2 -print_atoms | FileCheck %s +# +# Test linker can detect architecture without -arch option. +# + +--- !mach-o +arch: x86 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0xC3 ] +global-symbols: + - name: _foo + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + +... + + +# CHECK: defined-atoms: +# CHECK: - name: _foo diff --git a/test/mach-o/interposing-section.yaml b/test/mach-o/interposing-section.yaml new file mode 100644 index 0000000000000..856d4b91f3d28 --- /dev/null +++ b/test/mach-o/interposing-section.yaml @@ -0,0 +1,79 @@ +# RUN: lld -flavor darwin -arch x86_64 %s -dylib -o %t %p/Inputs/libSystem.yaml +# RUN: llvm-objdump -private-headers %t | FileCheck %s +# +# RUN: lld -flavor darwin -arch x86_64 %s -r -o %t1 +# RUN: llvm-objdump -private-headers %t1 | FileCheck %s +# +# Test that interposing section is preserved by linker. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x31, 0xC0, 0x5D, 0xE9, + 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000008 + type: X86_64_RELOC_BRANCH + length: 2 + pc-rel: true + extern: true + symbol: 2 + - segment: __DATA + section: __interpose + type: S_INTERPOSING + attributes: [ ] + alignment: 3 + address: 0x0000000000000010 + content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000008 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 2 + - offset: 0x00000000 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 0 +local-symbols: + - name: _my_open + type: N_SECT + sect: 1 + value: 0x0000000000000000 + - name: __interpose_open + type: N_SECT + sect: 2 + desc: [ N_NO_DEAD_STRIP ] + value: 0x0000000000000010 +undefined-symbols: + - name: _open + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + +--- !mach-o +arch: x86_64 +file-type: MH_DYLIB +install-name: /usr/lib/libSystem.B.dylib +exports: + - name: _open + +... + + +# CHECK: sectname __interposing +# CHECK: segname __DATA +# CHECK: type S_INTERPOSING + diff --git a/test/mach-o/keep_private_externs.yaml b/test/mach-o/keep_private_externs.yaml new file mode 100644 index 0000000000000..d67941a1c8eb5 --- /dev/null +++ b/test/mach-o/keep_private_externs.yaml @@ -0,0 +1,63 @@ +# RUN: lld -flavor darwin -arch x86_64 -r %s -o %t \ +# RUN: && llvm-nm -m %t | FileCheck %s +# +# RUN: lld -flavor darwin -arch x86_64 -r %s -o %t2 -keep_private_externs \ +# RUN: && llvm-nm -m %t2 | FileCheck -check-prefix=CHECK_KPE %s +# +# Test -keep_private_externs in -r mode. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x5D, 0xC3, 0x55, 0x48, + 0x89, 0xE5, 0x5D, 0xC3 ] + - segment: __DATA + section: __data + type: S_REGULAR + attributes: [ ] + alignment: 2 + address: 0x000000000000000C + content: [ 0x0A, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00 ] + +global-symbols: + - name: _a + type: N_SECT + scope: [ N_EXT ] + sect: 2 + value: 0x000000000000000C + - name: _b + type: N_SECT + scope: [ N_EXT, N_PEXT ] + sect: 2 + value: 0x0000000000000010 + - name: _bar + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000006 + - name: _foo + type: N_SECT + scope: [ N_EXT, N_PEXT ] + sect: 1 + value: 0x0000000000000000 + + +... + +# CHECK: (__DATA,__data) external _a +# CHECK: (__DATA,__data) non-external (was a private external) _b +# CHECK: (__TEXT,__text) external _bar +# CHECK: (__TEXT,__text) non-external (was a private external) _foo + +# CHECK_KPE: (__DATA,__data) external _a +# CHECK_KPE: (__DATA,__data) private external _b +# CHECK_KPE: (__TEXT,__text) external _bar +# CHECK_KPE: (__TEXT,__text) private external _foo diff --git a/test/mach-o/lazy-bind-x86_64.yaml b/test/mach-o/lazy-bind-x86_64.yaml new file mode 100644 index 0000000000000..54d787ce91bed --- /dev/null +++ b/test/mach-o/lazy-bind-x86_64.yaml @@ -0,0 +1,125 @@ +# REQUIRES: x86 + +# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t \ +# RUN: %p/Inputs/libSystem.yaml +# RUN: llvm-objdump -lazy-bind %t | FileCheck %s +# RUN: llvm-nm -m %t | FileCheck --check-prefix=CHECK-NM %s +# RUN: llvm-objdump -disassemble %t | FileCheck --check-prefix=CHECK-HELPERS %s +# RUN: llvm-objdump -private-headers %t | FileCheck --check-prefix=CHECK-DYLIBS %s +# +# Test that correct two-level namespace ordinals are used for lazy bindings. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x31, 0xC0, 0xE8, 0x00, + 0x00, 0x00, 0x00, 0x31, 0xC0, 0xE8, 0x00, 0x00, + 0x00, 0x00, 0x31, 0xC0, 0xE8, 0x00, 0x00, 0x00, + 0x00, 0x31, 0xC0, 0x5D, 0xC3 ] + relocations: + - offset: 0x00000015 + type: X86_64_RELOC_BRANCH + length: 2 + pc-rel: true + extern: true + symbol: 3 + - offset: 0x0000000E + type: X86_64_RELOC_BRANCH + length: 2 + pc-rel: true + extern: true + symbol: 2 + - offset: 0x00000007 + type: X86_64_RELOC_BRANCH + length: 2 + pc-rel: true + extern: true + symbol: 1 +global-symbols: + - name: _main + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: _bar + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: _baz + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: _foo + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + +--- !mach-o +arch: x86_64 +file-type: MH_DYLIB +install-name: /usr/lib/libbar.dylib +compat-version: 1.0 +current-version: 2.3 +exports: + - name: _bar + +--- !mach-o +arch: x86_64 +file-type: MH_DYLIB +install-name: /usr/lib/libfoo.dylib +compat-version: 2.0 +current-version: 3.4 +exports: + - name: _foo + +--- !mach-o +arch: x86_64 +file-type: MH_DYLIB +install-name: /usr/lib/libbaz.dylib +compat-version: 3.0 +current-version: 4.5 +exports: + - name: _baz + +... + + +# CHECK: libbar _bar +# CHECK: libbaz _baz +# CHECK: libfoo _foo + + +# CHECK-NM: (undefined) external _bar (from libbar) +# CHECK-NM: (undefined) external _baz (from libbaz) +# CHECK-NM: (undefined) external _foo (from libfoo) + + +# CHECK-HELPERS:Disassembly of section __TEXT,__stub_helper: +# CHECK-HELPERS: 68 00 00 00 00 pushq $0 +# CHECK-HELPERS: 68 10 00 00 00 pushq $16 +# CHECK-HELPERS: 68 20 00 00 00 pushq $32 + + +# CHECK-DYLIBS: cmd LC_LOAD_DYLIB +# CHECK-DYLIBS: name /usr/lib/libbar.dylib (offset 24) +# CHECK-DYLIBS: current version 2.3.0 +# CHECK-DYLIBS: compatibility version 1.0.0 +# CHECK-DYLIBS: cmd LC_LOAD_DYLIB +# CHECK-DYLIBS: name /usr/lib/libfoo.dylib (offset 24) +# CHECK-DYLIBS: current version 3.4.0 +# CHECK-DYLIBS: compatibility version 2.0.0 +# CHECK-DYLIBS: cmd LC_LOAD_DYLIB +# CHECK-DYLIBS: name /usr/lib/libbaz.dylib (offset 24) +# CHECK-DYLIBS: current version 4.5.0 +# CHECK-DYLIBS: compatibility version 3.0.0 + + diff --git a/test/mach-o/lib-search-paths.yaml b/test/mach-o/lib-search-paths.yaml new file mode 100644 index 0000000000000..5005f016857fa --- /dev/null +++ b/test/mach-o/lib-search-paths.yaml @@ -0,0 +1,16 @@ +# RUN: lld -flavor darwin -arch x86_64 %s -syslibroot %p/Inputs/lib-search-paths -lmyshared -lmystatic -lfile.o -r -print_atoms 2>&1 | FileCheck %s + +--- !native +undefined-atoms: + - name: _from_myshared + - name: _from_mystatic + - name: _from_fileo + +# CHECK: defined-atoms: +# CHECK: - name: _from_fileo +# CHECK: content: [ 2A, 00, 00, 00 ] +# CHECK: - name: _from_mystatic +# CHECK: content: [ 02, 00, 00, 00 ] +# CHECK: shared-library-atoms: +# CHECK: - name: _from_myshared +# CHECK: load-name: libmyshared.dylib diff --git a/test/mach-o/library-order.yaml b/test/mach-o/library-order.yaml new file mode 100644 index 0000000000000..23e9f6873134b --- /dev/null +++ b/test/mach-o/library-order.yaml @@ -0,0 +1,45 @@ +# RUN: lld -flavor darwin -arch x86_64 %p/Inputs/libfoo.a %s -o %t \ +# RUN: %p/Inputs/libSystem.yaml +# RUN: llvm-nm -m -n %t | FileCheck %s +# +# Test that if library is before object file on command line, it still is used. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 4 + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x83, 0xEC, 0x10, + 0xC7, 0x45, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xB0, + 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x31, 0xC0, + 0x48, 0x83, 0xC4, 0x10, 0x5D, 0xC3 ] + relocations: + - offset: 0x00000012 + type: X86_64_RELOC_BRANCH + length: 2 + pc-rel: true + extern: true + symbol: 1 +global-symbols: + - name: _main + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: _foo + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + +... + +# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _main +# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _foo diff --git a/test/mach-o/library-rescan.yaml b/test/mach-o/library-rescan.yaml new file mode 100644 index 0000000000000..a58d763fff005 --- /dev/null +++ b/test/mach-o/library-rescan.yaml @@ -0,0 +1,46 @@ +# RUN: lld -flavor darwin -arch x86_64 %p/Inputs/libfoo.a %p/Inputs/libbar.a \ +# RUN: %s -o %t %p/Inputs/libSystem.yaml +# RUN: llvm-nm -m -n %t | FileCheck %s +# +# Test that static libraries are automatically rescanned (bar needs foo). +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 4 + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x83, 0xEC, 0x10, + 0xC7, 0x45, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xB0, + 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x31, 0xC0, + 0x48, 0x83, 0xC4, 0x10, 0x5D, 0xC3 ] + relocations: + - offset: 0x00000012 + type: X86_64_RELOC_BRANCH + length: 2 + pc-rel: true + extern: true + symbol: 1 +global-symbols: + - name: _main + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: _bar + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + +... + +# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _main +# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _bar +# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _foo diff --git a/test/mach-o/libresolve-bizarre-root-override.yaml b/test/mach-o/libresolve-bizarre-root-override.yaml new file mode 100644 index 0000000000000..c65ca319432de --- /dev/null +++ b/test/mach-o/libresolve-bizarre-root-override.yaml @@ -0,0 +1,17 @@ +# RUN: not lld -flavor darwin -test_file_usage -v \ +# RUN: -path_exists /usr/lib \ +# RUN: -path_exists /Applications/MySDK/usr/local/lib \ +# RUN: -path_exists /Applications/MySDK/usr/lib \ +# RUN: -path_exists /Applications/MySDK/usr/lib/libSystem.dylib \ +# RUN: -syslibroot /Applications/MySDK \ +# RUN: -syslibroot / \ +# RUN: -lSystem \ +# RUN: 2>&1 | FileCheck %s + +# When the last -syslibroot is simply "/", all of them get discarded. So in this +# case, only /usr/lib should show up. + +# CHECK: Library search paths: +# CHECK: /usr/lib +# CHECK-NOT: /usr/local/lib +# CHECK: Unable to find library for -lSystem diff --git a/test/mach-o/libresolve-multiple-syslibroots.yaml b/test/mach-o/libresolve-multiple-syslibroots.yaml new file mode 100644 index 0000000000000..0b63eb64e7a92 --- /dev/null +++ b/test/mach-o/libresolve-multiple-syslibroots.yaml @@ -0,0 +1,17 @@ +# RUN: lld -flavor darwin -test_file_usage -v \ +# RUN: -path_exists /usr/lib \ +# RUN: -path_exists /Applications/MyFirstSDK/usr/local/lib \ +# RUN: -path_exists /Applications/MySecondSDK/usr/local/lib \ +# RUN: -path_exists /Applications/MyFirstSDK/usr/local/lib/libSystem.a \ +# RUN: -path_exists /Applications/MySecondSDK/usr/local/lib/libSystem.a \ +# RUN: -syslibroot /Applications/MyFirstSDK \ +# RUN: -syslibroot /Applications/MySecondSDK \ +# RUN: -lSystem \ +# RUN: 2>&1 | FileCheck %s + + +# CHECK: Library search paths: +# CHECK: /usr/lib +# CHECK: /Applications/MyFirstSDK/usr/local/lib +# CHECK: /Applications/MySecondSDK/usr/local/lib +# CHECK: Found library /Applications/MyFirstSDK/usr/local/lib/libSystem.a diff --git a/test/mach-o/libresolve-one-syslibroot.yaml b/test/mach-o/libresolve-one-syslibroot.yaml new file mode 100644 index 0000000000000..f9042fcfada21 --- /dev/null +++ b/test/mach-o/libresolve-one-syslibroot.yaml @@ -0,0 +1,25 @@ +# RUN: lld -flavor darwin -test_file_usage -v \ +# RUN: -path_exists /usr/lib \ +# RUN: -path_exists /Applications/MySDK/usr/local/lib \ +# RUN: -path_exists /Applications/MySDK/usr/local/lib/libSystem.a \ +# RUN: -path_exists /hasFoo \ +# RUN: -path_exists /hasFoo/foo.o \ +# RUN: -syslibroot /Applications/MySDK \ +# RUN: -L/hasFoo \ +# RUN: -lSystem -lfoo.o \ +# RUN: 2>&1 | FileCheck %s + +# When just one -syslibroot is specified, we apparently want to skip *system* +# paths that aren't found. User ones should still get added. In this case +# /usr/lib exists, but not the equivalent in the -syslibroot, so there should be +# no mention of /usr/lib. + +# CHECK: Library search paths: +# CHECK: /hasFoo +# CHECK-NOT: /usr/lib +# CHECK-NOT: /usr/local/lib +# CHECK: /Applications/MySDK/usr/local/lib +# CHECK-NOT: /usr/lib +# CHECK-NOT: /usr/local/lib +# CHECK: Found library /Applications/MySDK/usr/local/lib/libSystem.a +# CHECK: Found library /hasFoo/foo.o diff --git a/test/mach-o/libresolve-simple.yaml b/test/mach-o/libresolve-simple.yaml new file mode 100644 index 0000000000000..ffb045fa3e3c1 --- /dev/null +++ b/test/mach-o/libresolve-simple.yaml @@ -0,0 +1,21 @@ +# RUN: lld -flavor darwin -arch x86_64 -r -test_file_usage -v \ +# RUN: -path_exists /usr/lib \ +# RUN: -path_exists /usr/local/lib \ +# RUN: -path_exists /usr/lib/libSystem.dylib \ +# RUN: -path_exists hasFoo \ +# RUN: -path_exists hasFoo/libFoo.dylib \ +# RUN: -path_exists /hasBar \ +# RUN: -path_exists /hasBar/libBar.dylib \ +# RUN: -L hasFoo \ +# RUN: -L /hasBar \ +# RUN: -lSystem -lFoo -lBar \ +# RUN: 2>&1 | FileCheck %s + +# CHECK: Library search paths: +# CHECK: hasFoo +# CHECK: /hasBar +# CHECK: /usr/lib +# CHECK: /usr/local/lib +# CHECK: Found library /usr/lib/libSystem.dylib +# CHECK: Found library hasFoo/libFoo.dylib +# CHECK: Found library /hasBar/libBar.dylib diff --git a/test/mach-o/libresolve-user-paths.yaml b/test/mach-o/libresolve-user-paths.yaml new file mode 100644 index 0000000000000..9fe885671686e --- /dev/null +++ b/test/mach-o/libresolve-user-paths.yaml @@ -0,0 +1,20 @@ +# RUN: lld -flavor darwin -arch x86_64 -r -test_file_usage -v \ +# RUN: -path_exists hasFoo \ +# RUN: -path_exists hasFoo/libFoo.dylib \ +# RUN: -path_exists /hasBar \ +# RUN: -path_exists /hasBar/libBar.dylib \ +# RUN: -path_exists /SDK/hasFoo \ +# RUN: -path_exists /SDK/hasFoo/libFoo.dylib \ +# RUN: -path_exists /SDK/hasBar \ +# RUN: -path_exists /SDK/hasBar/libBar.dylib \ +# RUN: -syslibroot /SDK \ +# RUN: -L hasFoo \ +# RUN: -L /hasBar \ +# RUN: -lFoo -lBar \ +# RUN: 2>&1 | FileCheck %s + +# CHECK: Library search paths: +# CHECK: hasFoo +# CHECK: /SDK/hasBar +# CHECK: Found library hasFoo/libFoo.dylib +# CHECK: Found library /SDK/hasBar/libBar.dylib diff --git a/test/mach-o/libresolve-z.yaml b/test/mach-o/libresolve-z.yaml new file mode 100644 index 0000000000000..1df7eceac1e42 --- /dev/null +++ b/test/mach-o/libresolve-z.yaml @@ -0,0 +1,21 @@ +# RUN: lld -flavor darwin -arch x86_64 -r -test_file_usage -v \ +# RUN: -path_exists /usr/lib \ +# RUN: -path_exists /usr/local/lib \ +# RUN: -path_exists /usr/lib/libSystem.dylib \ +# RUN: -path_exists hasFoo \ +# RUN: -path_exists hasFoo/libFoo.dylib \ +# RUN: -path_exists /hasBar \ +# RUN: -path_exists /hasBar/libBar.dylib \ +# RUN: -L hasFoo \ +# RUN: -L /hasBar \ +# RUN: -Z \ +# RUN: -lFoo -lBar \ +# RUN: 2>&1 | FileCheck %s + +# CHECK: Library search paths: +# CHECK: hasFoo +# CHECK: /hasBar +# CHECK-NOT: /usr/lib +# CHECK-NOT: /usr/local/lib +# CHECK: Found library hasFoo/libFoo.dylib +# CHECK: Found library /hasBar/libBar.dylib diff --git a/test/mach-o/linker-as-ld.yaml b/test/mach-o/linker-as-ld.yaml new file mode 100644 index 0000000000000..2dd1f79818e1d --- /dev/null +++ b/test/mach-o/linker-as-ld.yaml @@ -0,0 +1,39 @@ +# REQUIRES: system-linker-mach-o +# +# RUN: mkdir -p %t.dir && cp `which lld` %t.dir/ld \ +# RUN: && %t.dir/ld -arch x86_64 -macosx_version_min 10.8 %s -o %t \ +# RUN: && llvm-nm %t | FileCheck %s +# +# Test linker run as "ld" on darwin works as darwin linker. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0xC3 ] +global-symbols: + - name: _main + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + +--- !mach-o +arch: x86_64 +file-type: MH_DYLIB +install-name: /usr/lib/libSystem.B.dylib +exports: + - name: dyld_stub_binder + +... + +# CHECK: T _main diff --git a/test/mach-o/lit.local.cfg b/test/mach-o/lit.local.cfg new file mode 100644 index 0000000000000..739a0994fdda7 --- /dev/null +++ b/test/mach-o/lit.local.cfg @@ -0,0 +1,4 @@ + +# mach-o test cases encode input files in yaml and use .yaml extension +config.suffixes = ['.yaml'] +config.excludes = ['Inputs'] diff --git a/test/mach-o/mh_bundle_header.yaml b/test/mach-o/mh_bundle_header.yaml new file mode 100644 index 0000000000000..558df2ca2e954 --- /dev/null +++ b/test/mach-o/mh_bundle_header.yaml @@ -0,0 +1,53 @@ +# RUN: lld -flavor darwin -arch x86_64 %s -bundle -o %t %p/Inputs/libSystem.yaml +# RUN: llvm-nm -m -n %t | FileCheck %s +# +# Test that __mh_bundle_header symbol is available for bundles +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x31, 0xC0, 0x5D, 0xC3 ] + - segment: __DATA + section: __data + type: S_REGULAR + attributes: [ ] + alignment: 3 + address: 0x0000000000000008 + content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000000 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 2 +global-symbols: + - name: _d + type: N_SECT + scope: [ N_EXT ] + sect: 2 + value: 0x0000000000000008 + - name: _foo + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: __mh_bundle_header + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + + +... + +# CHECK_NOT: __mh_bundle_header +# CHECK: _foo diff --git a/test/mach-o/mh_dylib_header.yaml b/test/mach-o/mh_dylib_header.yaml new file mode 100644 index 0000000000000..07429b30c9433 --- /dev/null +++ b/test/mach-o/mh_dylib_header.yaml @@ -0,0 +1,53 @@ +# RUN: lld -flavor darwin -arch x86_64 %s -dylib -o %t %p/Inputs/libSystem.yaml +# RUN: llvm-nm -m -n %t | FileCheck %s +# +# Test that __mh_dylib_header symbol is available for dylibs +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x31, 0xC0, 0x5D, 0xC3 ] + - segment: __DATA + section: __data + type: S_REGULAR + attributes: [ ] + alignment: 3 + address: 0x0000000000000008 + content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000000 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 2 +global-symbols: + - name: _d + type: N_SECT + scope: [ N_EXT ] + sect: 2 + value: 0x0000000000000008 + - name: _foo + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: __mh_dylib_header + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + + +... + +# CHECK_NOT: __mh_dylib_header +# CHECK: _foo diff --git a/test/mach-o/objc_export_list.yaml b/test/mach-o/objc_export_list.yaml new file mode 100644 index 0000000000000..5844812295be6 --- /dev/null +++ b/test/mach-o/objc_export_list.yaml @@ -0,0 +1,63 @@ +# RUN: lld -flavor darwin -arch x86_64 -dylib %s -o %t \ +# RUN: -exported_symbol .objc_class_name_Foo %p/Inputs/libSystem.yaml +# RUN: llvm-nm -m %t | FileCheck %s +# +# Test that exported objc classes can be specificed using old naming +# (.e.g .objc_class_name_Foo instead of _OBJC_CLASS_$_Foo) +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __DATA + section: __objc_data + type: S_REGULAR + attributes: [ ] + alignment: 3 + address: 0x0000000000000000 + content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000030 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 0 + - offset: 0x00000028 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 1 + - offset: 0x00000000 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 1 +global-symbols: + - name: '_OBJC_CLASS_$_Foo' + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + - name: '_OBJC_METACLASS_$_Foo' + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000028 +... + +# CHECK: (__DATA,__objc_data) external _OBJC_CLASS_$_Foo +# CHECK: (__DATA,__objc_data) external _OBJC_METACLASS_$_Foo diff --git a/test/mach-o/order_file-basic.yaml b/test/mach-o/order_file-basic.yaml new file mode 100644 index 0000000000000..3fea9be15601f --- /dev/null +++ b/test/mach-o/order_file-basic.yaml @@ -0,0 +1,75 @@ +# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/libSystem.yaml \ +# RUN: -order_file %p/Inputs/order_file-basic.order \ +# RUN: -force_load %p/Inputs/libfoo.a -o %t +# RUN: llvm-nm -m -n %t | FileCheck %s +# +# Test -order_file +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0xC3, 0xC3, 0xC3, 0xC3 ] + - segment: __DATA + section: __data + type: S_REGULAR + attributes: [ ] + alignment: 2 + address: 0x0000000000000014 + content: [ 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00 ] +global-symbols: + - name: _data1 + type: N_SECT + scope: [ N_EXT ] + sect: 2 + value: 0x0000000000000014 + - name: _data2 + type: N_SECT + scope: [ N_EXT ] + sect: 2 + value: 0x0000000000000018 + - name: _data3 + type: N_SECT + scope: [ N_EXT ] + sect: 2 + value: 0x000000000000001C + - name: _func1 + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + - name: _func2 + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000001 + - name: _func3 + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000002 + - name: _main + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000003 +... + + +# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _func2 +# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _foo +# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _func1 +# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _func3 +# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _main +# CHECK: {{[0-9a-f]+}} (__DATA,__data) external _data3 +# CHECK: {{[0-9a-f]+}} (__DATA,__data) external _data1 +# CHECK: {{[0-9a-f]+}} (__DATA,__data) external _data2 + diff --git a/test/mach-o/parse-aliases.yaml b/test/mach-o/parse-aliases.yaml new file mode 100644 index 0000000000000..457ea58133b95 --- /dev/null +++ b/test/mach-o/parse-aliases.yaml @@ -0,0 +1,90 @@ +# RUN: lld -flavor darwin -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s +# +# Test multiple labels to same address parse into aliases. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 4 + address: 0x0000000000000000 + content: [ 0xCC, 0xC3 ] +local-symbols: + - name: _pad + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + - name: _myStaticAlias1 + type: N_SECT + sect: 1 + value: 0x0000000000000001 + - name: _myStaticAlias3 + type: N_SECT + sect: 1 + value: 0x0000000000000001 + - name: _myStaticAlias2 + type: N_SECT + sect: 1 + value: 0x0000000000000001 +global-symbols: + - name: _myGlobalFunc1 + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000001 + - name: _myGlobalFunc2 + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000001 + - name: _myGlobalFunc3 + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000001 + - name: _myHiddenAlias1 + type: N_SECT + scope: [ N_EXT, N_PEXT ] + sect: 1 + value: 0x0000000000000001 + - name: _myHiddenAlias2 + type: N_SECT + scope: [ N_EXT, N_PEXT ] + sect: 1 + value: 0x0000000000000001 + - name: _myHiddenAlias3 + type: N_SECT + scope: [ N_EXT, N_PEXT ] + sect: 1 + value: 0x0000000000000001 +... + +# CHECK: defined-atoms: +# CHECK: - name: _pad +# CHECK: scope: global +# CHECK: content: [ CC ] +# CHECK: - name: _myStaticAlias1 +# CHECK: - name: _myStaticAlias2 +# CHECK: - name: _myStaticAlias3 +# CHECK: - name: _myHiddenAlias1 +# CHECK: scope: hidden +# CHECK: - name: _myHiddenAlias2 +# CHECK: scope: hidden +# CHECK: - name: _myHiddenAlias3 +# CHECK: scope: hidden +# CHECK: - name: _myGlobalFunc1 +# CHECK: scope: global +# CHECK: - name: _myGlobalFunc2 +# CHECK: scope: global +# CHECK: - name: _myGlobalFunc3 +# CHECK: scope: global +# CHECK: content: [ C3 ] diff --git a/test/mach-o/parse-arm-relocs.yaml b/test/mach-o/parse-arm-relocs.yaml new file mode 100644 index 0000000000000..c87c2a99b2157 --- /dev/null +++ b/test/mach-o/parse-arm-relocs.yaml @@ -0,0 +1,818 @@ +# RUN: lld -flavor darwin -arch armv7 -r -print_atoms %s -o %t | FileCheck %s +# RUN: lld -flavor darwin -arch armv7 -r -print_atoms %t -o %t2 | FileCheck %s +# +# Test parsing of armv7 relocations. +# +# + +--- !mach-o +arch: armv7 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 2 + address: 0x0000000000000000 + content: [ 0x00, 0xF0, 0x4E, 0xF8, 0x00, 0xF0, 0x4E, 0xF8, + 0xFF, 0xF7, 0xFA, 0xFF, 0xFF, 0xF7, 0xFA, 0xFF, + 0xFF, 0xF7, 0xF6, 0xBF, 0x40, 0xF2, 0x72, 0x01, + 0xC0, 0xF2, 0x00, 0x01, 0x40, 0xF2, 0x7A, 0x02, + 0xC0, 0xF2, 0x00, 0x02, 0x40, 0xF2, 0x29, 0x01, + 0xC0, 0xF2, 0x00, 0x01, 0x79, 0x44, 0x40, 0xF2, + 0xA0, 0x03, 0xC0, 0xF2, 0x00, 0x03, 0x40, 0xF2, + 0xA8, 0x04, 0xC0, 0xF2, 0x00, 0x04, 0x40, 0xF2, + 0x57, 0x03, 0xC0, 0xF2, 0x00, 0x03, 0x40, 0xF2, + 0x00, 0x05, 0xC0, 0xF2, 0x00, 0x05, 0x40, 0xF2, + 0x08, 0x06, 0xC0, 0xF2, 0x00, 0x06, 0xC0, 0x46, + 0x10, 0x00, 0x00, 0xEB, 0x10, 0x00, 0x00, 0xEB, + 0xE6, 0xFF, 0xFF, 0xEB, 0xE6, 0xFF, 0xFF, 0xEB, + 0xE4, 0xFF, 0xFF, 0xEA, 0x20, 0x10, 0x00, 0xE3, + 0x00, 0x10, 0x40, 0xE3, 0x28, 0x20, 0x00, 0xE3, + 0x00, 0x20, 0x40, 0xE3, 0x0F, 0x10, 0x81, 0xE0, + 0xA0, 0x30, 0x00, 0xE3, 0x00, 0x30, 0x40, 0xE3, + 0xA8, 0x40, 0x00, 0xE3, 0x00, 0x40, 0x40, 0xE3, + 0x00, 0x50, 0x00, 0xE3, 0x00, 0x50, 0x40, 0xE3, + 0x08, 0x60, 0x00, 0xE3, 0x00, 0x60, 0x40, 0xE3 ] + relocations: + - offset: 0x0000009C + type: ARM_RELOC_HALF + length: 1 + pc-rel: false + extern: true + symbol: 4 + - offset: 0x00000008 + type: ARM_RELOC_PAIR + length: 1 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x00000098 + type: ARM_RELOC_HALF + length: 0 + pc-rel: false + extern: true + symbol: 4 + - offset: 0x00000000 + type: ARM_RELOC_PAIR + length: 0 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x00000094 + type: ARM_RELOC_HALF + length: 1 + pc-rel: false + extern: true + symbol: 4 + - offset: 0x00000000 + type: ARM_RELOC_PAIR + length: 1 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x00000090 + type: ARM_RELOC_HALF + length: 0 + pc-rel: false + extern: true + symbol: 4 + - offset: 0x00000000 + type: ARM_RELOC_PAIR + length: 0 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x0000008C + scattered: true + type: ARM_RELOC_HALF + length: 1 + pc-rel: false + value: 0x000000A0 + - offset: 0x000000A8 + type: ARM_RELOC_PAIR + length: 1 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x00000088 + scattered: true + type: ARM_RELOC_HALF + length: 0 + pc-rel: false + value: 0x000000A0 + - offset: 0x00000000 + type: ARM_RELOC_PAIR + length: 0 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x00000084 + type: ARM_RELOC_HALF + length: 1 + pc-rel: false + extern: false + symbol: 2 + - offset: 0x000000A0 + type: ARM_RELOC_PAIR + length: 1 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x00000080 + type: ARM_RELOC_HALF + length: 0 + pc-rel: false + extern: false + symbol: 2 + - offset: 0x00000000 + type: ARM_RELOC_PAIR + length: 0 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x00000078 + scattered: true + type: ARM_RELOC_HALF_SECTDIFF + length: 1 + pc-rel: false + value: 0x000000A0 + - offset: 0x00000028 + scattered: true + type: ARM_RELOC_PAIR + length: 1 + pc-rel: false + value: 0x00000080 + - offset: 0x00000074 + scattered: true + type: ARM_RELOC_HALF_SECTDIFF + length: 0 + pc-rel: false + value: 0x000000A0 + - offset: 0x00000000 + scattered: true + type: ARM_RELOC_PAIR + length: 0 + pc-rel: false + value: 0x00000080 + - offset: 0x00000070 + scattered: true + type: ARM_RELOC_HALF_SECTDIFF + length: 1 + pc-rel: false + value: 0x000000A0 + - offset: 0x00000020 + scattered: true + type: ARM_RELOC_PAIR + length: 1 + pc-rel: false + value: 0x00000080 + - offset: 0x0000006C + scattered: true + type: ARM_RELOC_HALF_SECTDIFF + length: 0 + pc-rel: false + value: 0x000000A0 + - offset: 0x00000000 + scattered: true + type: ARM_RELOC_PAIR + length: 0 + pc-rel: false + value: 0x00000080 + - offset: 0x00000068 + type: ARM_RELOC_BR24 + length: 2 + pc-rel: true + extern: true + symbol: 4 + - offset: 0x00000064 + type: ARM_RELOC_BR24 + length: 2 + pc-rel: true + extern: true + symbol: 4 + - offset: 0x00000060 + type: ARM_RELOC_BR24 + length: 2 + pc-rel: true + extern: true + symbol: 4 + - offset: 0x0000005C + scattered: true + type: ARM_RELOC_BR24 + length: 2 + pc-rel: true + value: 0x000000A0 + - offset: 0x00000058 + type: ARM_RELOC_BR24 + length: 2 + pc-rel: true + extern: false + symbol: 2 + - offset: 0x00000052 + type: ARM_RELOC_HALF + length: 3 + pc-rel: false + extern: true + symbol: 4 + - offset: 0x00000008 + type: ARM_RELOC_PAIR + length: 3 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x0000004E + type: ARM_RELOC_HALF + length: 2 + pc-rel: false + extern: true + symbol: 4 + - offset: 0x00000000 + type: ARM_RELOC_PAIR + length: 2 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x0000004A + type: ARM_RELOC_HALF + length: 3 + pc-rel: false + extern: true + symbol: 4 + - offset: 0x00000000 + type: ARM_RELOC_PAIR + length: 3 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x00000046 + type: ARM_RELOC_HALF + length: 2 + pc-rel: false + extern: true + symbol: 4 + - offset: 0x00000000 + type: ARM_RELOC_PAIR + length: 2 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x00000042 + type: ARM_RELOC_HALF + length: 3 + pc-rel: false + extern: false + symbol: 1 + - offset: 0x00000057 + type: ARM_RELOC_PAIR + length: 3 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x0000003E + type: ARM_RELOC_HALF + length: 2 + pc-rel: false + extern: false + symbol: 1 + - offset: 0x00000000 + type: ARM_RELOC_PAIR + length: 2 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x0000003A + scattered: true + type: ARM_RELOC_HALF + length: 3 + pc-rel: false + value: 0x000000A0 + - offset: 0x000000A8 + type: ARM_RELOC_PAIR + length: 3 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x00000036 + scattered: true + type: ARM_RELOC_HALF + length: 2 + pc-rel: false + value: 0x000000A0 + - offset: 0x00000000 + type: ARM_RELOC_PAIR + length: 2 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x00000032 + type: ARM_RELOC_HALF + length: 3 + pc-rel: false + extern: false + symbol: 2 + - offset: 0x000000A0 + type: ARM_RELOC_PAIR + length: 3 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x0000002E + type: ARM_RELOC_HALF + length: 2 + pc-rel: false + extern: false + symbol: 2 + - offset: 0x00000000 + type: ARM_RELOC_PAIR + length: 2 + pc-rel: false + extern: false + symbol: 16777215 + - offset: 0x00000028 + scattered: true + type: ARM_RELOC_HALF_SECTDIFF + length: 3 + pc-rel: false + value: 0x00000056 + - offset: 0x00000028 + scattered: true + type: ARM_RELOC_PAIR + length: 3 + pc-rel: false + value: 0x0000002E + - offset: 0x00000024 + scattered: true + type: ARM_RELOC_HALF_SECTDIFF + length: 2 + pc-rel: false + value: 0x00000056 + - offset: 0x00000000 + scattered: true + type: ARM_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x0000002E + - offset: 0x00000020 + scattered: true + type: ARM_RELOC_HALF_SECTDIFF + length: 3 + pc-rel: false + value: 0x000000A0 + - offset: 0x0000007A + scattered: true + type: ARM_RELOC_PAIR + length: 3 + pc-rel: false + value: 0x0000002E + - offset: 0x0000001C + scattered: true + type: ARM_RELOC_HALF_SECTDIFF + length: 2 + pc-rel: false + value: 0x000000A0 + - offset: 0x00000000 + scattered: true + type: ARM_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x0000002E + - offset: 0x00000018 + scattered: true + type: ARM_RELOC_HALF_SECTDIFF + length: 3 + pc-rel: false + value: 0x000000A0 + - offset: 0x00000072 + scattered: true + type: ARM_RELOC_PAIR + length: 3 + pc-rel: false + value: 0x0000002E + - offset: 0x00000014 + scattered: true + type: ARM_RELOC_HALF_SECTDIFF + length: 2 + pc-rel: false + value: 0x000000A0 + - offset: 0x00000000 + scattered: true + type: ARM_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x0000002E + - offset: 0x00000010 + type: ARM_THUMB_RELOC_BR22 + length: 2 + pc-rel: true + extern: true + symbol: 4 + - offset: 0x0000000C + type: ARM_THUMB_RELOC_BR22 + length: 2 + pc-rel: true + extern: true + symbol: 4 + - offset: 0x00000008 + type: ARM_THUMB_RELOC_BR22 + length: 2 + pc-rel: true + extern: true + symbol: 4 + - offset: 0x00000004 + scattered: true + type: ARM_THUMB_RELOC_BR22 + length: 2 + pc-rel: true + value: 0x000000A0 + - offset: 0x00000000 + type: ARM_THUMB_RELOC_BR22 + length: 2 + pc-rel: true + extern: false + symbol: 2 + - segment: __DATA + section: __data + type: S_REGULAR + attributes: [ ] + address: 0x00000000000000A0 + content: [ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xA4, 0xFF, 0xFF, 0xFF, + 0xA4, 0xFF, 0xFF, 0xFF, 0x45, 0xFF, 0xFF, 0xFF, + 0x45, 0xFF, 0xFF, 0xFF ] + relocations: + - offset: 0x00000020 + scattered: true + type: ARM_RELOC_SECTDIFF + length: 2 + pc-rel: false + value: 0x00000000 + - offset: 0x00000000 + scattered: true + type: ARM_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x000000C0 + - offset: 0x0000001C + scattered: true + type: ARM_RELOC_SECTDIFF + length: 2 + pc-rel: false + value: 0x00000000 + - offset: 0x00000000 + scattered: true + type: ARM_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x000000BC + - offset: 0x00000018 + scattered: true + type: ARM_RELOC_SECTDIFF + length: 2 + pc-rel: false + value: 0x00000058 + - offset: 0x00000000 + scattered: true + type: ARM_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x000000B8 + - offset: 0x00000014 + scattered: true + type: ARM_RELOC_SECTDIFF + length: 2 + pc-rel: false + value: 0x00000058 + - offset: 0x00000000 + scattered: true + type: ARM_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x000000B4 + - offset: 0x00000010 + type: ARM_RELOC_VANILLA + length: 2 + pc-rel: false + extern: true + symbol: 4 + - offset: 0x0000000C + type: ARM_RELOC_VANILLA + length: 2 + pc-rel: false + extern: true + symbol: 4 + - offset: 0x00000008 + scattered: true + type: ARM_RELOC_VANILLA + length: 2 + pc-rel: false + value: 0x00000000 + - offset: 0x00000004 + type: ARM_RELOC_VANILLA + length: 2 + pc-rel: false + extern: false + symbol: 1 +local-symbols: + - name: _foo_thumb + type: N_SECT + sect: 1 + desc: [ N_ARM_THUMB_DEF ] + value: 0x0000000000000000 + - name: _x + type: N_SECT + sect: 2 + value: 0x00000000000000A0 + - name: _t1 + type: N_SECT + sect: 1 + desc: [ N_ARM_THUMB_DEF ] + value: 0x0000000000000056 + - name: _foo_arm + type: N_SECT + sect: 1 + value: 0x0000000000000058 +undefined-symbols: + - name: _undef + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 +... + +# CHECK: defined-atoms: +# CHECK: - name: _x +# CHECK: type: data +# CHECK: references: +# CHECK: - kind: pointer32 +# CHECK: offset: 4 +# CHECK: target: _foo_thumb +# CHECK-NOT: addend: +# CHECK: - kind: pointer32 +# CHECK: offset: 8 +# CHECK: target: _foo_thumb +# CHECK: addend: 4 +# CHECK: - kind: pointer32 +# CHECK: offset: 12 +# CHECK: target: _undef +# CHECK-NOT: addend: +# CHECK: - kind: pointer32 +# CHECK: offset: 16 +# CHECK: target: _undef +# CHECK: addend: 4 +# CHECK: - kind: delta32 +# CHECK: offset: 20 +# CHECK: target: _foo_arm +# CHECK-NOT: addend: +# CHECK: - kind: delta32 +# CHECK: offset: 24 +# CHECK: target: _foo_arm +# CHECK: addend: 4 +# CHECK: - kind: delta32 +# CHECK: offset: 28 +# CHECK: target: _foo_thumb +# CHECK-NOT: addend: +# CHECK: - kind: delta32 +# CHECK: offset: 32 +# CHECK: target: _foo_thumb +# CHECK: addend: 4 +# CHECK: - name: _foo_thumb +# CHECK: references: +# CHECK: - kind: modeThumbCode +# CHECK: offset: 0 +# CHECK: - kind: thumb_bl22 +# CHECK: offset: 0 +# CHECK: target: _x +# CHECK-NOT: addend: +# CHECK: - kind: thumb_bl22 +# CHECK: offset: 4 +# CHECK: target: _x +# CHECK: addend: 4 +# CHECK: - kind: thumb_bl22 +# CHECK: offset: 8 +# CHECK: target: _undef +# CHECK-NOT: addend: +# CHECK: - kind: thumb_bl22 +# CHECK: offset: 12 +# CHECK: target: _undef +# CHECK: addend: 4 +# CHECK: - kind: thumb_b22 +# CHECK: offset: 16 +# CHECK: target: _undef +# CHECK-NOT: addend: +# CHECK: - kind: thumb_movw_funcRel +# CHECK: offset: 20 +# CHECK: target: _x +# CHECK: addend: -46 +# CHECK: - kind: thumb_movt_funcRel +# CHECK: offset: 24 +# CHECK: target: _x +# CHECK: addend: -46 +# CHECK: - kind: thumb_movw_funcRel +# CHECK: offset: 28 +# CHECK: target: _x +# CHECK: addend: -38 +# CHECK: - kind: thumb_movt_funcRel +# CHECK: offset: 32 +# CHECK: target: _x +# CHECK: addend: -38 +# CHECK: - kind: thumb_movw_funcRel +# CHECK: offset: 36 +# CHECK: target: _t1 +# CHECK: addend: -46 +# CHECK: - kind: thumb_movt_funcRel +# CHECK: offset: 40 +# CHECK: target: _t1 +# CHECK: addend: -46 +# CHECK: - kind: thumb_movw +# CHECK: offset: 46 +# CHECK: target: _x +# CHECK-NOT: addend: +# CHECK: - kind: thumb_movt +# CHECK: offset: 50 +# CHECK: target: _x +# CHECK-NOT: addend: +# CHECK: - kind: thumb_movw +# CHECK: offset: 54 +# CHECK: target: _x +# CHECK: addend: 8 +# CHECK: - kind: thumb_movt +# CHECK: offset: 58 +# CHECK: target: _x +# CHECK: addend: 8 +# CHECK: - kind: thumb_movw +# CHECK: offset: 62 +# CHECK: target: _t1 +# CHECK-NOT: addend: +# CHECK: - kind: thumb_movt +# CHECK: offset: 66 +# CHECK: target: _t1 +# CHECK-NOT: addend: +# CHECK: - kind: thumb_movw +# CHECK: offset: 70 +# CHECK: target: _undef +# CHECK-NOT: addend: +# CHECK: - kind: thumb_movt +# CHECK: offset: 74 +# CHECK: target: _undef +# CHECK-NOT: addend: +# CHECK: - kind: thumb_movw +# CHECK: offset: 78 +# CHECK: target: _undef +# CHECK: addend: 8 +# CHECK: - kind: thumb_movt +# CHECK: offset: 82 +# CHECK: target: _undef +# CHECK: addend: 8 +# CHECK: - name: _t1 +# CHECK: content: [ C0, 46 ] +# CHECK: references: +# CHECK: - kind: modeThumbCode +# CHECK: offset: 0 +# CHECK: - name: _foo_arm +# CHECK: references: +# CHECK-NOT: - kind: modeThumbCode +# CHECK: - kind: arm_bl24 +# CHECK: offset: 0 +# CHECK: target: _x +# CHECK-NOT: addend: +# CHECK: - kind: arm_bl24 +# CHECK: offset: 4 +# CHECK: target: _x +# CHECK: addend: 4 +# CHECK: - kind: arm_bl24 +# CHECK: offset: 8 +# CHECK: target: _undef +# CHECK-NOT: addend: +# CHECK: - kind: arm_bl24 +# CHECK: offset: 12 +# CHECK: target: _undef +# CHECK: addend: 4 +# CHECK: - kind: arm_b24 +# CHECK: offset: 16 +# CHECK: target: _undef +# CHECK-NOT: addend: +# CHECK: - kind: arm_movw_funcRel +# CHECK: offset: 20 +# CHECK: target: _x +# CHECK: addend: -40 +# CHECK: - kind: arm_movt_funcRel +# CHECK: offset: 24 +# CHECK: target: _x +# CHECK: addend: -40 +# CHECK: - kind: arm_movw_funcRel +# CHECK: offset: 28 +# CHECK: target: _x +# CHECK: addend: -32 +# CHECK: - kind: arm_movt_funcRel +# CHECK: offset: 32 +# CHECK: target: _x +# CHECK: addend: -32 +# CHECK: - kind: arm_movw +# CHECK: offset: 40 +# CHECK: target: _x +# CHECK-NOT: addend: +# CHECK: - kind: arm_movt +# CHECK: offset: 44 +# CHECK: target: _x +# CHECK-NOT: addend: +# CHECK: - kind: arm_movw +# CHECK: offset: 48 +# CHECK: target: _x +# CHECK: addend: 8 +# CHECK: - kind: arm_movt +# CHECK: offset: 52 +# CHECK: target: _x +# CHECK: addend: 8 +# CHECK: - kind: arm_movw +# CHECK: offset: 56 +# CHECK: target: _undef +# CHECK-NOT: addend: +# CHECK: - kind: arm_movt +# CHECK: offset: 60 +# CHECK: target: _undef +# CHECK-NOT: addend: +# CHECK: - kind: arm_movw +# CHECK: offset: 64 +# CHECK: target: _undef +# CHECK: addend: 8 +# CHECK: - kind: arm_movt +# CHECK: offset: 68 +# CHECK: target: _undef +# CHECK: addend: 8 +# CHECK: undefined-atoms: +# CHECK: - name: _undef + + + + +# .align 2 +# .code 16 +# .thumb_func _foo_thumb +#_foo_thumb: +# bl _x +# bl _x+4 +# bl _undef +# bl _undef+4 +# b _undef +# movw r1, :lower16:(_x-L1) +# movt r1, :upper16:(_x-L1) +# movw r2, :lower16:(_x+8-L1) +# movt r2, :upper16:(_x+8-L1) +# movw r1, :lower16:(_t1-L1) +# movt r1, :upper16:(_t1-L1) +# add r1, pc +#L1: +# movw r3, :lower16:_x +# movt r3, :upper16:_x +# movw r4, :lower16:_x+8 +# movt r4, :upper16:_x+8 +# movw r3, :lower16:_t1 +# movt r3, :upper16:_t1 +# movw r5, :lower16:_undef +# movt r5, :upper16:_undef +# movw r6, :lower16:_undef+8 +# movt r6, :upper16:_undef+8 +# +# .thumb_func _t1 +#_t1: +# nop +# +# +# .code 32 +# .align 2 +#_foo_arm: +# bl _x +# bl _x+4 +# bl _undef +# bl _undef+4 +# b _undef +# movw r1, :lower16:(_x-L2) +# movt r1, :upper16:(_x-L2) +# movw r2, :lower16:(_x+8-L2) +# movt r2, :upper16:(_x+8-L2) +# add r1, pc +#L2: +# movw r3, :lower16:_x +# movt r3, :upper16:_x +# movw r4, :lower16:_x+8 +# movt r4, :upper16:_x+8 +# movw r5, :lower16:_undef +# movt r5, :upper16:_undef +# movw r6, :lower16:_undef+8 +# movt r6, :upper16:_undef+8 +# +# +# .data +#_x: .long 0 +# .long _foo_thumb +# .long _foo_thumb+4 +# .long _undef +# .long _undef+4 +# .long _foo_arm - . +# .long _foo_arm+4- . +# .long _foo_thumb - . +# .long _foo_thumb+4 - . +# diff --git a/test/mach-o/parse-cfstring32.yaml b/test/mach-o/parse-cfstring32.yaml new file mode 100644 index 0000000000000..657e733a779b9 --- /dev/null +++ b/test/mach-o/parse-cfstring32.yaml @@ -0,0 +1,94 @@ +# RUN: lld -flavor darwin -arch i386 -r -print_atoms %s -o %t | FileCheck %s +# +# Test parsing of mach-o functions. +# + +--- !mach-o +arch: x86 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __cstring + type: S_CSTRING_LITERALS + attributes: [ ] + address: 0x0000000000000000 + content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x00, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x00 ] + - segment: __DATA + section: __cfstring + type: S_REGULAR + attributes: [ ] + alignment: 3 + address: 0x0000000000000010 + content: [ 0x00, 0x00, 0x00, 0x00, 0xC8, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC8, 0x07, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000018 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: false + extern: false + symbol: 1 + - offset: 0x00000010 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: false + extern: true + symbol: 0 + - offset: 0x00000008 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: false + extern: false + symbol: 1 + - offset: 0x00000000 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: false + extern: true + symbol: 0 +undefined-symbols: + - name: ___CFConstantStringClassReference + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 +... + +# CHECK: defined-atoms: +# CHECK: - ref-name: [[STR1:L[L0-9]+]] +# CHECK: scope: hidden +# CHECK: type: c-string +# CHECK: content: [ 68, 65, 6C, 6C, 6F, 00 ] +# CHECK: merge: by-content +# CHECK: - ref-name: [[STR2:L[L0-9]+]] +# CHECK: scope: hidden +# CHECK: type: c-string +# CHECK: content: [ 74, 68, 65, 72, 65, 00 ] +# CHECK: merge: by-content +# CHECK: - scope: hidden +# CHECK: type: cfstring +# CHECK: merge: by-content +# CHECK: references: +# CHECK: - kind: pointer32 +# CHECK: offset: 0 +# CHECK: target: ___CFConstantStringClassReference +# CHECK: - kind: pointer32 +# CHECK: offset: 8 +# CHECK: target: [[STR1]] +# CHECK: - scope: hidden +# CHECK: type: cfstring +# CHECK: merge: by-content +# CHECK: references: +# CHECK: - kind: pointer32 +# CHECK: offset: 0 +# CHECK: target: ___CFConstantStringClassReference +# CHECK: - kind: pointer32 +# CHECK: offset: 8 +# CHECK: target: [[STR2]] +# CHECK:undefined-atoms: +# CHECK: - name: ___CFConstantStringClassReference diff --git a/test/mach-o/parse-cfstring64.yaml b/test/mach-o/parse-cfstring64.yaml new file mode 100644 index 0000000000000..fbd674d90d997 --- /dev/null +++ b/test/mach-o/parse-cfstring64.yaml @@ -0,0 +1,108 @@ +# RUN: lld -flavor darwin -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s +# +# Test parsing of CFString constants. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __cstring + type: S_CSTRING_LITERALS + attributes: [ ] + address: 0x0000000000000000 + content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x00, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x00 ] + - segment: __DATA + section: __cfstring + type: S_REGULAR + attributes: [ ] + alignment: 4 + address: 0x0000000000000010 + content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000030 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 1 + - offset: 0x00000020 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 2 + - offset: 0x00000010 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 0 + - offset: 0x00000000 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 2 +local-symbols: + - name: Lstr1 + type: N_SECT + sect: 1 + value: 0x0000000000000000 + - name: Lstr2 + type: N_SECT + sect: 1 + value: 0x0000000000000006 +undefined-symbols: + - name: ___CFConstantStringClassReference + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 +... + +# CHECK:defined-atoms: +# CHECK: - ref-name: L000 +# CHECK: scope: hidden +# CHECK: type: c-string +# CHECK: content: [ 68, 65, 6C, 6C, 6F, 00 ] +# CHECK: merge: by-content +# CHECK: - ref-name: L001 +# CHECK: scope: hidden +# CHECK: type: c-string +# CHECK: content: [ 74, 68, 65, 72, 65, 00 ] +# CHECK: merge: by-content +# CHECK: - scope: hidden +# CHECK: type: cfstring +# CHECK: merge: by-content +# CHECK: references: +# CHECK: - kind: pointer64 +# CHECK: offset: 0 +# CHECK: target: ___CFConstantStringClassReference +# CHECK: - kind: pointer64 +# CHECK: offset: 16 +# CHECK: target: L000 +# CHECK: - scope: hidden +# CHECK: type: cfstring +# CHECK: merge: by-content +# CHECK: references: +# CHECK: - kind: pointer64 +# CHECK: offset: 0 +# CHECK: target: ___CFConstantStringClassReference +# CHECK: - kind: pointer64 +# CHECK: offset: 16 +# CHECK: target: L001 +# CHECK:undefined-atoms: +# CHECK: - name: ___CFConstantStringClassReference + diff --git a/test/mach-o/parse-compact-unwind32.yaml b/test/mach-o/parse-compact-unwind32.yaml new file mode 100644 index 0000000000000..ff613f0809bb4 --- /dev/null +++ b/test/mach-o/parse-compact-unwind32.yaml @@ -0,0 +1,72 @@ +# RUN: lld -flavor darwin -arch i386 -r -print_atoms %s -o %t | FileCheck %s +# +# Test parsing of __LD/__compact_unwind (compact unwind) section. +# + +--- !mach-o +arch: x86 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 4 + address: 0x0000000000000000 + content: [ 0x55, 0x89, 0xE5, 0xB8, 0x0A, 0x00, 0x00, 0x00, + 0x5D, 0xC3, 0x55, 0x89, 0xE5, 0xB8, 0x0A, 0x00, + 0x00, 0x00, 0x5D, 0xC3 ] + - segment: __LD + section: __compact_unwind + type: S_REGULAR + attributes: [ ] + alignment: 2 + address: 0x000000000000001C + content: [ 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000014 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: false + extern: false + symbol: 1 + - offset: 0x00000000 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: false + extern: false + symbol: 1 +global-symbols: + - name: __Z3barv + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x000000000000000A + - name: __Z3foov + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +... + +# CHECK: defined-atoms: +# CHECK: - type: compact-unwind +# CHECK: content: [ 00, 00, 00, 00, 0A, 00, 00, 00, 00, 00, 00, 01, +# CHECK: 00, 00, 00, 00, 00, 00, 00, 00 ] +# CHECK: - type: compact-unwind +# CHECK: content: [ 10, 00, 00, 00, 0A, 00, 00, 00, 00, 00, 00, 01, +# CHECK: 00, 00, 00, 00, 00, 00, 00, 00 ] +# CHECK: - name: __Z3foov +# CHECK: scope: global +# CHECK: content: [ 55, 89, E5, B8, 0A, 00, 00, 00, 5D, C3 ] +# CHECK: - name: __Z3barv +# CHECK: scope: global +# CHECK: content: [ 55, 89, E5, B8, 0A, 00, 00, 00, 5D, C3 ] + diff --git a/test/mach-o/parse-compact-unwind64.yaml b/test/mach-o/parse-compact-unwind64.yaml new file mode 100644 index 0000000000000..b61961a3d0b06 --- /dev/null +++ b/test/mach-o/parse-compact-unwind64.yaml @@ -0,0 +1,76 @@ +# RUN: lld -flavor darwin -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s +# +# Test parsing of __LD/__compact_unwind (compact unwind) section. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 4 + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0xB8, 0x0A, 0x00, 0x00, + 0x00, 0x5D, 0xC3, 0x55, 0x48, 0x89, 0xE5, 0xB8, + 0x0A, 0x00, 0x00, 0x00, 0x5D, 0xC3 ] + - segment: __LD + section: __compact_unwind + type: S_REGULAR + attributes: [ ] + alignment: 3 + address: 0x0000000000000020 + content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000020 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: false + symbol: 1 + - offset: 0x00000000 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: false + symbol: 1 +global-symbols: + - name: __Z3barv + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + - name: __Z3foov + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x000000000000000B +... + +# CHECK: defined-atoms: +# CHECK: - type: compact-unwind +# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00, 0B, 00, 00, 00, +# CHECK: 00, 00, 00, 01, 00, 00, 00, 00, 00, 00, 00, 00, +# CHECK: 00, 00, 00, 00, 00, 00, 00, 00 ] +# CHECK: - type: compact-unwind +# CHECK: content: [ 10, 00, 00, 00, 00, 00, 00, 00, 0B, 00, 00, 00, +# CHECK: 00, 00, 00, 01, 00, 00, 00, 00, 00, 00, 00, 00, +# CHECK: 00, 00, 00, 00, 00, 00, 00, 00 ] +# CHECK: - name: __Z3barv +# CHECK: scope: global +# CHECK: content: [ 55, 48, 89, E5, B8, 0A, 00, 00, 00, 5D, C3 ] +# CHECK: - name: __Z3foov +# CHECK: scope: global +# CHECK: content: [ 55, 48, 89, E5, B8, 0A, 00, 00, 00, 5D, C3 ] diff --git a/test/mach-o/parse-data-in-code-armv7.yaml b/test/mach-o/parse-data-in-code-armv7.yaml new file mode 100644 index 0000000000000..29b483e7d7236 --- /dev/null +++ b/test/mach-o/parse-data-in-code-armv7.yaml @@ -0,0 +1,157 @@ +# RUN: lld -flavor darwin -arch armv7 -r -print_atoms %s -o %t | FileCheck %s +# RUN: lld -flavor darwin -arch armv7 -r -print_atoms %t -o %t2 | FileCheck %s +# RUN: lld -flavor darwin -arch armv7 -dylib %s -o %t3.dylib %p/Inputs/libSystem.yaml \ +# RUN: && llvm-objdump -macho -private-headers %t3.dylib | FileCheck --check-prefix=CHECK2 %s +# +# Test parsing LC_DATA_IN_CODE +# +# + +--- !mach-o +arch: armv7 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 2 + address: 0x0000000000000000 + content: [ 0x00, 0xBF, 0x00, 0xBF, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x00, 0xBF, + 0x00, 0xF0, 0x20, 0xE3, 0x0A, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x20, 0xE3 ] +local-symbols: + - name: _foo_thumb + type: N_SECT + sect: 1 + desc: [ N_ARM_THUMB_DEF ] + value: 0x0000000000000000 + - name: _foo_arm + type: N_SECT + sect: 1 + value: 0x0000000000000018 +dataInCode: + - offset: 0x00000004 + length: 0x0004 + kind: DICE_KIND_DATA + - offset: 0x00000008 + length: 0x0004 + kind: DICE_KIND_JUMP_TABLE32 + - offset: 0x0000000C + length: 0x0004 + kind: DICE_KIND_JUMP_TABLE16 + - offset: 0x00000010 + length: 0x0004 + kind: DICE_KIND_JUMP_TABLE8 + - offset: 0x0000001C + length: 0x0004 + kind: DICE_KIND_DATA + - offset: 0x00000020 + length: 0x0004 + kind: DICE_KIND_JUMP_TABLE32 + - offset: 0x00000024 + length: 0x0004 + kind: DICE_KIND_JUMP_TABLE16 + - offset: 0x00000028 + length: 0x0004 + kind: DICE_KIND_JUMP_TABLE8 +... + + + +# CHECK: defined-atoms: +# CHECK: - name: _foo_thumb +# CHECK: references: +# CHECK: - kind: modeThumbCode +# CHECK: offset: 0 +# CHECK: - kind: modeData +# CHECK: offset: 4 +# CHECK: addend: 1 +# CHECK: - kind: modeData +# CHECK: offset: 8 +# CHECK: addend: 4 +# CHECK: - kind: modeData +# CHECK: offset: 12 +# CHECK: addend: 3 +# CHECK: - kind: modeData +# CHECK: offset: 16 +# CHECK: addend: 2 +# CHECK: - kind: modeThumbCode +# CHECK: offset: 20 +# CHECK: - name: _foo_arm +# CHECK: references: +# CHECK: - kind: modeData +# CHECK: offset: 4 +# CHECK: addend: 1 +# CHECK: - kind: modeData +# CHECK: offset: 8 +# CHECK: addend: 4 +# CHECK: - kind: modeData +# CHECK: offset: 12 +# CHECK: addend: 3 +# CHECK: - kind: modeData +# CHECK: offset: 16 +# CHECK: addend: 2 +# CHECK: - kind: modeArmCode +# CHECK: offset: 20 + + +# CHECK2: cmd LC_DATA_IN_CODE +# CHECK2: cmdsize 16 +# CHECK2: datasize 64 + + +# .code 16 +# .thumb_func _foo_thumb +#_foo_thumb: +# nop +# nop +# +# .data_region +# .long 0 +# .end_data_region +# +# .data_region jt32 +# .long 1 +# .end_data_region +# +# .data_region jt16 +# .long 2 +# .end_data_region +# +# .data_region jt8 +# .long 3 +# .end_data_region +# +# nop +# nop +# +# +# +# .code 32 +# .align 2 +#_foo_arm: +# nop +# +# .data_region +# .long 10 +# .end_data_region +# +# .data_region jt32 +# .long 11 +# .end_data_region +# +# .data_region jt16 +# .long 12 +# .end_data_region +# +# .data_region jt8 +# .long 13 +# .end_data_region +# +# nop +# diff --git a/test/mach-o/parse-data-in-code-x86.yaml b/test/mach-o/parse-data-in-code-x86.yaml new file mode 100644 index 0000000000000..43934440f2a0c --- /dev/null +++ b/test/mach-o/parse-data-in-code-x86.yaml @@ -0,0 +1,77 @@ +# RUN: lld -flavor darwin -arch i386 -r -print_atoms %s -o %t | FileCheck %s \ +# RUN: && lld -flavor darwin -arch i386 -r -print_atoms %t -o %t2 | FileCheck %s +# +# Test parsing LC_DATA_IN_CODE +# +# + +--- !mach-o +arch: x86 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x90, 0x90, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0x03, 0x00, + 0x00, 0x00 ] +local-symbols: + - name: _func1 + type: N_SECT + sect: 1 + value: 0x0000000000000000 + - name: _func2 + type: N_SECT + sect: 1 + value: 0x000000000000000B +dataInCode: + - offset: 0x00000002 + length: 0x0008 + kind: DICE_KIND_JUMP_TABLE32 + - offset: 0x0000000E + length: 0x0004 + kind: DICE_KIND_JUMP_TABLE32 +... + + + +# CHECK: defined-atoms: +# CHECK: - name: _func1 +# CHECK: references: +# CHECK: - kind: modeData +# CHECK: offset: 2 +# CHECK: addend: 4 +# CHECK: - kind: modeCode +# CHECK: offset: 10 +# CHECK: - name: _func2 +# CHECK: references: +# CHECK: - kind: modeData +# CHECK: offset: 3 +# CHECK: addend: 4 +# CHECK-NOT: - kind: modeData + + + + +# +#_func1: +# nop +# nop +# .data_region jt32 +# .long 1 +# .long 2 +# .end_data_region +# nop +# +# +# _func2: +# nop +# nop +# nop +# .data_region jt32 +# .long 3 +# .end_data_region +# diff --git a/test/mach-o/parse-data-relocs-arm64.yaml b/test/mach-o/parse-data-relocs-arm64.yaml new file mode 100644 index 0000000000000..d02422f6a6f97 --- /dev/null +++ b/test/mach-o/parse-data-relocs-arm64.yaml @@ -0,0 +1,222 @@ +# RUN: lld -flavor darwin -arch arm64 -r -print_atoms %s -o %t | FileCheck %s +# RUN: lld -flavor darwin -arch arm64 -r -print_atoms %t -o %t2 | FileCheck %s +# +# Test parsing and writing of arm64 data relocations. +# +# The first step tests if the supplied mach-o file is parsed into the correct +# set of references. The second step verifies relocations can be round-tripped +# by writing to a new .o file, then parsing that file which should result in +# the same references. +# +#_test: + + +--- !mach-o +arch: arm64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __DATA + section: __data + type: S_REGULAR + attributes: [ ] + address: 0x0000000000000000 + content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xDC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC0, 0xFF, 0xFF, 0xFF, 0xBE, 0xFF, 0xFF, 0xFF, + 0xB0, 0xFF, 0xFF, 0xFF ] + relocations: + - offset: 0x00000050 + type: ARM64_RELOC_POINTER_TO_GOT + length: 2 + pc-rel: true + extern: true + symbol: 1 + - offset: 0x0000004C + type: ARM64_RELOC_SUBTRACTOR + length: 2 + pc-rel: false + extern: true + symbol: 1 + - offset: 0x0000004C + type: ARM64_RELOC_UNSIGNED + length: 2 + pc-rel: false + extern: true + symbol: 1 + - offset: 0x00000048 + type: ARM64_RELOC_SUBTRACTOR + length: 2 + pc-rel: false + extern: true + symbol: 1 + - offset: 0x00000048 + type: ARM64_RELOC_UNSIGNED + length: 2 + pc-rel: false + extern: true + symbol: 1 + - offset: 0x00000040 + type: ARM64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 1 + - offset: 0x00000038 + type: ARM64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: false + symbol: 1 + - offset: 0x00000030 + type: ARM64_RELOC_SUBTRACTOR + length: 3 + pc-rel: false + extern: true + symbol: 1 + - offset: 0x00000030 + type: ARM64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 1 + - offset: 0x00000028 + type: ARM64_RELOC_SUBTRACTOR + length: 3 + pc-rel: false + extern: true + symbol: 1 + - offset: 0x00000028 + type: ARM64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 1 + - offset: 0x00000020 + type: ARM64_RELOC_SUBTRACTOR + length: 3 + pc-rel: false + extern: true + symbol: 1 + - offset: 0x00000020 + type: ARM64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 1 + - offset: 0x00000018 + type: ARM64_RELOC_POINTER_TO_GOT + length: 3 + pc-rel: false + extern: true + symbol: 1 + - offset: 0x00000010 + type: ARM64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 1 + - offset: 0x00000008 + type: ARM64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 1 +local-symbols: + - name: _v1 + type: N_SECT + sect: 1 + value: 0x0000000000000008 +undefined-symbols: + - name: _foo + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 +... + +# CHECK: defined-atoms: +# CHECK: - ref-name: L000 +# CHECK: type: data +# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ] +# CHECK: - name: _v1 +# CHECK: type: data +# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00, 08, 00, 00, 00, +# CHECK: 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, +# CHECK: 00, 00, 00, 00, 00, 00, 00, 00, E0, FF, FF, FF, +# CHECK: FF, FF, FF, FF, DC, FF, FF, FF, FF, FF, FF, FF, +# CHECK: 00, 00, 00, 00, 00, 00, 00, 00, 04, 00, 00, 00, +# CHECK: 00, 00, 00, 00, C0, FF, FF, FF, BE, FF, FF, FF, +# CHECK: B0, FF, FF, FF ] +# CHECK: references: +# CHECK: - kind: pointer64 +# CHECK: offset: 0 +# CHECK: target: _foo +# CHECK-NOT: addend: +# CHECK: - kind: pointer64 +# CHECK: offset: 8 +# CHECK: target: _foo +# CHECK: addend: 8 +# CHECK: - kind: pointer64ToGOT +# CHECK: offset: 16 +# CHECK: target: _foo +# CHECK-NOT: addend: +# CHECK: - kind: delta64 +# CHECK: offset: 24 +# CHECK: target: _foo +# CHECK: addend: 24 +# CHECK: - kind: delta64 +# CHECK: offset: 32 +# CHECK: target: _foo +# CHECK-NOT: addend: +# CHECK: - kind: delta64 +# CHECK: offset: 40 +# CHECK: target: _foo +# CHECK: addend: 4 +# CHECK: - kind: pointer64 +# CHECK: offset: 48 +# CHECK: target: L000 +# CHECK-NOT: addend: +# CHECK: - kind: pointer64 +# CHECK: offset: 56 +# CHECK: target: _foo +# CHECK: addend: 4 +# CHECK: - kind: delta32 +# CHECK: offset: 64 +# CHECK: target: _foo +# CHECK-NOT: addend: +# CHECK: - kind: delta32 +# CHECK: offset: 68 +# CHECK: target: _foo +# CHECK: addend: 2 +# CHECK: - kind: delta32ToGOT +# CHECK: offset: 72 +# CHECK: target: _foo +# CHECK-NOT: addend: +# CHECK: undefined-atoms: +# CHECK: - name: _foo + + + +# .data +#Lanon: +# .quad 0 +#_v1: +# .quad _foo +# .quad _foo + 8 +# .quad _foo@GOT +# .quad _foo + 24 - . +# .quad _foo - . +# .quad _foo + 4 - . +# .quad Lanon +# .quad Lanon + 4 +# .long _foo - . +# .long _foo +2 - . +# .long _foo@GOT - . + diff --git a/test/mach-o/parse-data-relocs-x86_64.yaml b/test/mach-o/parse-data-relocs-x86_64.yaml new file mode 100644 index 0000000000000..ae93c1bb75d92 --- /dev/null +++ b/test/mach-o/parse-data-relocs-x86_64.yaml @@ -0,0 +1,230 @@ + +# RUN: lld -flavor darwin -arch x86_64 -r %s -o %t -print_atoms | FileCheck %s \ +# RUN: && lld -flavor darwin -arch x86_64 %t -r -print_atoms -o %t2 | FileCheck %s +# +# Test parsing and writing of x86_64 text relocations. +# +# The first step tests if the supplied mach-o file is parsed into the correct +# set of references. The second step verifies relocations can be round-tripped +# by writing to a new .o file, then parsing that file which should result in +# the same references. +# +#_foo: +# ret +# +#_bar: +# ret +# +# .section __DATA,__custom +#L1: +# .quad 0 +# +# .data +#_d: +# .quad _foo +# .quad _foo+4 +# .quad _foo - . +# .quad L1 +# .quad L1 + 2 +# .quad _foo - . +# .quad _foo + 4 - . +# .quad L1 - . +# .long _foo - . +# .long _foo + 4 - . +# .long L1 - . +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0xC3, 0xC3 ] + - segment: __DATA + section: __custom + type: S_REGULAR + attributes: [ ] + address: 0x0000000000000002 + content: [ 0x00, 0x00, 0x00, 0x00 ] + - segment: __DATA + section: __data + type: S_REGULAR + attributes: [ ] + alignment: 3 + address: 0x0000000000000008 + content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xDC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xD2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC8, 0xFF, 0xFF, 0xFF, 0xC8, 0xFF, 0xFF, 0xFF, + 0xC2, 0xFF, 0xFF, 0xFF ] + relocations: + - offset: 0x00000040 + type: X86_64_RELOC_SUBTRACTOR + length: 2 + pc-rel: false + extern: true + symbol: 2 + - offset: 0x00000040 + type: X86_64_RELOC_UNSIGNED + length: 2 + pc-rel: false + extern: false + symbol: 2 + - offset: 0x0000003C + type: X86_64_RELOC_SUBTRACTOR + length: 2 + pc-rel: false + extern: true + symbol: 2 + - offset: 0x0000003C + type: X86_64_RELOC_UNSIGNED + length: 2 + pc-rel: false + extern: true + symbol: 0 + - offset: 0x00000038 + type: X86_64_RELOC_SUBTRACTOR + length: 2 + pc-rel: false + extern: true + symbol: 2 + - offset: 0x00000038 + type: X86_64_RELOC_UNSIGNED + length: 2 + pc-rel: false + extern: true + symbol: 0 + - offset: 0x00000030 + type: X86_64_RELOC_SUBTRACTOR + length: 3 + pc-rel: false + extern: true + symbol: 2 + - offset: 0x00000030 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: false + symbol: 2 + - offset: 0x00000028 + type: X86_64_RELOC_SUBTRACTOR + length: 3 + pc-rel: false + extern: true + symbol: 2 + - offset: 0x00000028 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 0 + - offset: 0x00000020 + type: X86_64_RELOC_SUBTRACTOR + length: 3 + pc-rel: false + extern: true + symbol: 2 + - offset: 0x00000020 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 0 + - offset: 0x00000018 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: false + symbol: 2 + - offset: 0x00000010 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: false + symbol: 2 + - offset: 0x00000008 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 0 + - offset: 0x00000000 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 0 +local-symbols: + - name: _foo + type: N_SECT + sect: 1 + value: 0x0000000000000000 + - name: _bar + type: N_SECT + sect: 1 + value: 0x0000000000000001 + - name: _d + type: N_SECT + sect: 3 + value: 0x0000000000000008 +... + + +# CHECK: defined-atoms: +# CHECK: - name: _d +# CHECK: type: data +# CHECK: references: +# CHECK: - kind: pointer64 +# CHECK: offset: 0 +# CHECK: target: _foo +# CHECK: - kind: pointer64 +# CHECK: offset: 8 +# CHECK: target: _foo +# CHECK: addend: 4 +# CHECK: - kind: pointer64Anon +# CHECK: offset: 16 +# CHECK: target: L003 +# CHECK: - kind: pointer64Anon +# CHECK: offset: 24 +# CHECK: target: L003 +# CHECK: addend: 2 +# CHECK: - kind: delta64 +# CHECK: offset: 32 +# CHECK: target: _foo +# CHECK: - kind: delta64 +# CHECK: offset: 40 +# CHECK: target: _foo +# CHECK: addend: 4 +# CHECK: - kind: delta64Anon +# CHECK: offset: 48 +# CHECK: target: L003 +# CHECK: - kind: delta32 +# CHECK: offset: 56 +# CHECK: target: _foo +# CHECK: - kind: delta32 +# CHECK: offset: 60 +# CHECK: target: _foo +# CHECK: addend: 4 +# CHECK: - kind: delta32Anon +# CHECK: offset: 64 +# CHECK: target: L003 +# CHECK: - name: _foo +# CHECK: content: [ C3 ] +# CHECK: - name: _bar +# CHECK: content: [ C3 ] +# CHECK: - ref-name: L003 +# CHECK: type: unknown +# CHECK: content: [ 00, 00, 00, 00 ] +# CHECK: section-choice: custom-required +# CHECK: section-name: __DATA/__custom diff --git a/test/mach-o/parse-data.yaml b/test/mach-o/parse-data.yaml new file mode 100644 index 0000000000000..61d77290d108b --- /dev/null +++ b/test/mach-o/parse-data.yaml @@ -0,0 +1,119 @@ +# RUN: lld -flavor darwin -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s +# +# Test parsing of mach-o data symbols. +# +# long a = 0x0807060504030201; +# int b = 0x14131211; +# int c = 0x24232221; +# static int s1; +# static int s2 = 0x34333231; +# +# + + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __DATA + section: __data + type: S_REGULAR + attributes: [ ] + alignment: 3 + address: 0x0000000000000000 + content: [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x11, 0x12, 0x13, 0x14, 0x21, 0x22, 0x23, 0x24, + 0x31, 0x32, 0x33, 0x34, 0x41, 0x42, 0x43, 0x44 ] + - segment: __CUST + section: __custom + type: S_REGULAR + attributes: [ ] + alignment: 3 + address: 0x0000000000000018 + content: [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 ] + - segment: __DATA + section: __bss + type: S_ZEROFILL + attributes: [ ] + alignment: 2 + address: 0x0000000000000020 + size: 4 +local-symbols: + - name: _s1 + type: N_SECT + sect: 3 + value: 0x0000000000000020 + - name: _s2 + type: N_SECT + sect: 1 + value: 0x0000000000000010 +global-symbols: + - name: _a + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + - name: _b + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000008 + - name: _c + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x000000000000000C + - name: _cWeak + type: N_SECT + scope: [ N_EXT ] + sect: 1 + desc: [ N_WEAK_DEF ] + value: 0x0000000000000014 + - name: _kustom + type: N_SECT + scope: [ N_EXT ] + sect: 2 + value: 0x0000000000000018 +... + +# CHECK: defined-atoms: + +# CHECK: - name: _a +# CHECK: scope: global +# CHECK: type: data +# CHECK: content: [ 01, 02, 03, 04, 05, 06, 07, 08 ] + +# CHECK: - name: _b +# CHECK: scope: global +# CHECK: type: data +# CHECK: content: [ 11, 12, 13, 14 ] + +# CHECK: - name: _c +# CHECK: scope: global +# CHECK: type: data +# CHECK: content: [ 21, 22, 23, 24 ] + +# CHECK: - name: _s2 +# CHECK: type: data +# CHECK: content: [ 31, 32, 33, 34 ] + +# CHECK: - name: _cWeak +# CHECK: scope: global +# CHECK: type: data +# CHECK: content: [ 41, 42, 43, 44 ] +# CHECK: merge: as-weak + +# CHECK: - name: _s1 +# CHECK: type: zero-fill +# CHECK: size: 4 + +# CHECK: - name: _kustom +# CHECK: scope: global +# CHECK: type: unknown +# CHECK: content: [ 01, 02, 03, 04, 05, 06, 07, 08 ] +# CHECK: section-choice: custom-required +# CHECK: section-name: __CUST/__custom + diff --git a/test/mach-o/parse-eh-frame-x86-anon.yaml b/test/mach-o/parse-eh-frame-x86-anon.yaml new file mode 100644 index 0000000000000..9e3adaea18498 --- /dev/null +++ b/test/mach-o/parse-eh-frame-x86-anon.yaml @@ -0,0 +1,129 @@ +# RUN: lld -flavor darwin -arch i386 -r -print_atoms %s -o %t | FileCheck %s +# +# Test parsing of new __eh_frame (dwarf unwind) section that has no .eh labels +# and no relocations. +# + +--- !mach-o +arch: x86 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x89, 0xE5, 0x56, 0x83, 0xEC, 0x14, 0xE8, + 0x00, 0x00, 0x00, 0x00, 0x5E, 0xC7, 0x04, 0x24, + 0x04, 0x00, 0x00, 0x00, 0xE8, 0xE7, 0xFF, 0xFF, + 0xFF, 0xC7, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x8B, + 0x8E, 0x38, 0x00, 0x00, 0x00, 0x89, 0x4C, 0x24, + 0x04, 0x89, 0x04, 0x24, 0xC7, 0x44, 0x24, 0x08, + 0x00, 0x00, 0x00, 0x00, 0xE8, 0xC7, 0xFF, 0xFF, + 0xFF, 0x55, 0x89, 0xE5, 0x83, 0xEC, 0x08, 0xE8, + 0xBC, 0xFF, 0xFF, 0xFF ] + relocations: + - offset: 0x00000040 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: true + extern: false + symbol: 1 + - offset: 0x00000035 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: true + extern: true + symbol: 4 + - offset: 0x00000021 + scattered: true + type: GENERIC_RELOC_LOCAL_SECTDIFF + length: 2 + pc-rel: false + value: 0x00000044 + - offset: 0x00000000 + scattered: true + type: GENERIC_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x0000000C + - offset: 0x00000015 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: true + extern: true + symbol: 3 + - segment: __IMPORT + section: __pointers + type: S_NON_LAZY_SYMBOL_POINTERS + attributes: [ ] + address: 0x0000000000000044 + content: [ 0x00, 0x00, 0x00, 0x00 ] + indirect-syms: [ 5 ] + - segment: __TEXT + section: __eh_frame + type: S_REGULAR + attributes: [ ] + alignment: 2 + address: 0x0000000000000048 + content: [ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x7A, 0x52, 0x00, 0x01, 0x7C, 0x08, 0x01, + 0x10, 0x0C, 0x05, 0x04, 0x88, 0x01, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x98, 0xFF, 0xFF, 0xFF, 0x39, 0x00, 0x00, 0x00, + 0x00, 0x41, 0x0E, 0x08, 0x84, 0x02, 0x42, 0x0D, + 0x04, 0x44, 0x86, 0x03, 0x18, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0xB5, 0xFF, 0xFF, 0xFF, + 0x0B, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0E, 0x08, + 0x84, 0x02, 0x42, 0x0D, 0x04, 0x00, 0x00, 0x00 ] +global-symbols: + - name: __Z3barv + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000039 + - name: __Z3foov + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: __ZTIi + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: ___cxa_allocate_exception + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: ___cxa_throw + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 +... + +# CHECK: defined-atoms: +# CHECK: - ref-name: [[CIE:L[L0-9]+]] +# CHECK: type: unwind-cfi +# CHECK: content: +# CHECK: - type: unwind-cfi +# CHECK: content: +# CHECK: references: +# CHECK: - kind: negDelta32 +# CHECK: offset: 4 +# CHECK: target: [[CIE]] +# CHECK: - kind: delta32 +# CHECK: offset: 8 +# CHECK: target: __Z3foov +# CHECK: - type: unwind-cfi +# CHECK: content: +# CHECK: references: +# CHECK: - kind: negDelta32 +# CHECK: offset: 4 +# CHECK: target: [[CIE]] +# CHECK: - kind: delta32 +# CHECK: offset: 8 +# CHECK: target: __Z3barv + diff --git a/test/mach-o/parse-eh-frame-x86-labeled.yaml b/test/mach-o/parse-eh-frame-x86-labeled.yaml new file mode 100644 index 0000000000000..b07a534e64934 --- /dev/null +++ b/test/mach-o/parse-eh-frame-x86-labeled.yaml @@ -0,0 +1,193 @@ +# RUN: lld -flavor darwin -arch i386 -r -print_atoms %s -o %t | FileCheck %s +# +# Test parsing of old __eh_frame (dwarf unwind) section that has .eh labels +# and relocations. +# + +--- !mach-o +arch: x86 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x89, 0xE5, 0x56, 0x83, 0xEC, 0x14, 0xE8, + 0x00, 0x00, 0x00, 0x00, 0x5E, 0xC7, 0x04, 0x24, + 0x04, 0x00, 0x00, 0x00, 0xE8, 0xE7, 0xFF, 0xFF, + 0xFF, 0xC7, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x8B, + 0x8E, 0x38, 0x00, 0x00, 0x00, 0x89, 0x4C, 0x24, + 0x04, 0x89, 0x04, 0x24, 0xC7, 0x44, 0x24, 0x08, + 0x00, 0x00, 0x00, 0x00, 0xE8, 0xC7, 0xFF, 0xFF, + 0xFF, 0x55, 0x89, 0xE5, 0x83, 0xEC, 0x08, 0xE8, + 0xBC, 0xFF, 0xFF, 0xFF ] + relocations: + - offset: 0x00000040 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: true + extern: false + symbol: 1 + - offset: 0x00000035 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: true + extern: true + symbol: 7 + - offset: 0x00000021 + scattered: true + type: GENERIC_RELOC_LOCAL_SECTDIFF + length: 2 + pc-rel: false + value: 0x00000044 + - offset: 0x00000000 + scattered: true + type: GENERIC_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x0000000C + - offset: 0x00000015 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: true + extern: true + symbol: 6 + - segment: __IMPORT + section: __pointers + type: S_NON_LAZY_SYMBOL_POINTERS + attributes: [ ] + address: 0x0000000000000044 + content: [ 0x00, 0x00, 0x00, 0x00 ] + indirect-syms: [ 5 ] + - segment: __TEXT + section: __eh_frame + type: S_REGULAR + attributes: [ ] + alignment: 2 + address: 0x0000000000000048 + content: [ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x7A, 0x52, 0x00, 0x01, 0x7C, 0x08, 0x01, + 0x10, 0x0C, 0x05, 0x04, 0x88, 0x01, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x98, 0xFF, 0xFF, 0xFF, 0x39, 0x00, 0x00, 0x00, + 0x00, 0x41, 0x0E, 0x08, 0x84, 0x02, 0x42, 0x0D, + 0x04, 0x44, 0x86, 0x03, 0x18, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0xB5, 0xFF, 0xFF, 0xFF, + 0x0B, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0E, 0x08, + 0x84, 0x02, 0x42, 0x0D, 0x04, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x0000001C + scattered: true + type: GENERIC_RELOC_LOCAL_SECTDIFF + length: 2 + pc-rel: false + value: 0x00000064 + - offset: 0x00000000 + scattered: true + type: GENERIC_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x00000048 + - offset: 0x00000020 + scattered: true + type: GENERIC_RELOC_SECTDIFF + length: 2 + pc-rel: false + value: 0x00000000 + - offset: 0x00000000 + scattered: true + type: GENERIC_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x00000068 + - offset: 0x00000038 + scattered: true + type: GENERIC_RELOC_LOCAL_SECTDIFF + length: 2 + pc-rel: false + value: 0x00000080 + - offset: 0x00000000 + scattered: true + type: GENERIC_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x00000048 + - offset: 0x0000003C + scattered: true + type: GENERIC_RELOC_SECTDIFF + length: 2 + pc-rel: false + value: 0x00000039 + - offset: 0x00000000 + scattered: true + type: GENERIC_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x00000084 +local-symbols: + - name: EH_frame0 + type: N_SECT + sect: 3 + value: 0x0000000000000048 +global-symbols: + - name: __Z3barv + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000039 + - name: __Z3barv.eh + type: N_SECT + scope: [ N_EXT ] + sect: 3 + value: 0x000000000000007C + - name: __Z3foov + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + - name: __Z3foov.eh + type: N_SECT + scope: [ N_EXT ] + sect: 3 + value: 0x0000000000000060 +undefined-symbols: + - name: __ZTIi + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: ___cxa_allocate_exception + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: ___cxa_throw + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 +... + +# CHECK: defined-atoms: +# CHECK: - ref-name: [[CIE:L[L0-9]+]] +# CHECK: type: unwind-cfi +# CHECK: content: +# CHECK: - type: unwind-cfi +# CHECK: content: +# CHECK: references: +# CHECK: - kind: negDelta32 +# CHECK: offset: 4 +# CHECK: target: [[CIE]] +# CHECK: - kind: delta32 +# CHECK: offset: 8 +# CHECK: target: __Z3foov +# CHECK: - type: unwind-cfi +# CHECK: content: +# CHECK: references: +# CHECK: - kind: negDelta32 +# CHECK: offset: 4 +# CHECK: target: [[CIE]] +# CHECK: - kind: delta32 +# CHECK: offset: 8 +# CHECK: target: __Z3barv + diff --git a/test/mach-o/parse-eh-frame.yaml b/test/mach-o/parse-eh-frame.yaml new file mode 100644 index 0000000000000..69324800e9912 --- /dev/null +++ b/test/mach-o/parse-eh-frame.yaml @@ -0,0 +1,88 @@ +# RUN: lld -flavor darwin -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s +# +# Test parsing of __eh_frame (dwarf unwind) section. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0xB8, 0x09, 0x00, 0x00, + 0x00, 0x5D, 0xC3, 0x55, 0x48, 0x89, 0xE5, 0xB8, + 0x0A, 0x00, 0x00, 0x00, 0x5D, 0xC3 ] + - segment: __TEXT + section: __eh_frame + type: S_COALESCED + attributes: [ ] + alignment: 3 + address: 0x0000000000000058 + content: [ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x7A, 0x52, 0x00, 0x01, 0x78, 0x10, 0x01, + 0x10, 0x0C, 0x07, 0x08, 0x90, 0x01, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x88, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x41, 0x0E, 0x10, 0x86, 0x02, 0x43, 0x0D, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, + 0x6B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x41, 0x0E, 0x10, 0x86, 0x02, 0x43, 0x0D, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] +global-symbols: + - name: __Z3barv + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + - name: __Z3foov + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x000000000000000B +... + +# CHECK: defined-atoms: +# CHECK: - ref-name: [[CIE:L[0-9]+]] +# CHECK: type: unwind-cfi +# CHECK: content: [ 14, 00, 00, 00, 00, 00, 00, 00, 01, 7A, 52, 00, +# CHECK: 01, 78, 10, 01, 10, 0C, 07, 08, 90, 01, 00, 00 ] +# CHECK: - type: unwind-cfi +# CHECK: content: [ 24, 00, 00, 00, 1C, 00, 00, 00, 88, FF, FF, FF, +# CHECK: FF, FF, FF, FF, 0B, 00, 00, 00, 00, 00, 00, 00, +# CHECK: 00, 41, 0E, 10, 86, 02, 43, 0D, 06, 00, 00, 00, +# CHECK: 00, 00, 00, 00 ] +# CHECK: references: +# CHECK: - kind: negDelta32 +# CHECK: offset: 4 +# CHECK: target: [[CIE]] +# CHECK: - kind: unwindFDEToFunction +# CHECK: offset: 8 +# CHECK: target: __Z3barv +# CHECK: - type: unwind-cfi +# CHECK: content: [ 24, 00, 00, 00, 44, 00, 00, 00, 6B, FF, FF, FF, +# CHECK: FF, FF, FF, FF, 0B, 00, 00, 00, 00, 00, 00, 00, +# CHECK: 00, 41, 0E, 10, 86, 02, 43, 0D, 06, 00, 00, 00, +# CHECK: 00, 00, 00, 00 ] +# CHECK: references: +# CHECK: - kind: negDelta32 +# CHECK: offset: 4 +# CHECK: target: [[CIE]] +# CHECK: - kind: unwindFDEToFunction +# CHECK: offset: 8 +# CHECK: target: __Z3foov +# CHECK: - name: __Z3barv +# CHECK: scope: global +# CHECK: content: [ 55, 48, 89, E5, B8, 09, 00, 00, 00, 5D, C3 ] +# CHECK: - name: __Z3foov +# CHECK: scope: global +# CHECK: content: [ 55, 48, 89, E5, B8, 0A, 00, 00, 00, 5D, C3 ] + diff --git a/test/mach-o/parse-function.yaml b/test/mach-o/parse-function.yaml new file mode 100644 index 0000000000000..1bc9878c70872 --- /dev/null +++ b/test/mach-o/parse-function.yaml @@ -0,0 +1,100 @@ +# RUN: lld -flavor darwin -arch x86_64 -r %s -o %t +# RUN: lld -flavor darwin -arch x86_64 -r %t -print_atoms -o %t2 | FileCheck %s +# +# Test parsing of mach-o functions. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 4 + address: 0x0000000000000000 + content: [ 0xCC, 0xC3, 0x90, 0xC3, 0x90, 0x90, 0xC3, 0x90, + 0x90, 0x90, 0xC3, 0x90, 0x90, 0x90, 0x90, 0xC3, + 0xCC, 0x31, 0xC0, 0xC3 ] +local-symbols: + - name: _myStatic + type: N_SECT + sect: 1 + value: 0x000000000000000B +global-symbols: + - name: _myGlobal + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000001 + - name: _myGlobalWeak + type: N_SECT + scope: [ N_EXT ] + sect: 1 + desc: [ N_WEAK_DEF ] + value: 0x0000000000000002 + - name: _myHidden + type: N_SECT + scope: [ N_EXT, N_PEXT ] + sect: 1 + value: 0x0000000000000004 + - name: _myHiddenWeak + type: N_SECT + scope: [ N_EXT, N_PEXT ] + sect: 1 + desc: [ N_WEAK_DEF ] + value: 0x0000000000000007 + - name: _myStripNot + type: N_SECT + scope: [ N_EXT ] + sect: 1 + desc: [ N_NO_DEAD_STRIP ] + value: 0x0000000000000010 + - name: _myResolver + type: N_SECT + scope: [ N_EXT ] + sect: 1 + desc: [ N_SYMBOL_RESOLVER ] + value: 0x0000000000000011 +... + +# CHECK-NOT: name: +# CHECK: content: [ CC ] + +# CHECK: name: _myGlobal +# CHECK: scope: global +# CHECK: content: [ C3 ] + +# CHECK: name: _myGlobalWeak +# CHECK: scope: global +# CHECK: content: [ 90, C3 ] +# CHECK: merge: as-weak + +# CHECK: name: _myHidden +# CHECK: scope: hidden +# CHECK: content: [ 90, 90, C3 ] + +# CHECK: name: _myHiddenWeak +# CHECK: scope: hidden +# CHECK: content: [ 90, 90, 90, C3 ] +# CHECK: merge: as-weak + +# CHECK: name: _myStatic +# CHECK-NOT: scope: global +# CHECK-NOT: scope: hidden +# CHECK: content: [ 90, 90, 90, 90, C3 ] + +# CHECK: name: _myStripNot +# CHECK: scope: global +# CHECK: content: [ CC ] +# CHECK: dead-strip: never + +# CHECK: name: _myResolver +# CHECK: scope: global +# CHECK: type: resolver +# CHECK: content: [ 31, C0, C3 ] + diff --git a/test/mach-o/parse-initializers32.yaml b/test/mach-o/parse-initializers32.yaml new file mode 100644 index 0000000000000..ede7b90e2cb9e --- /dev/null +++ b/test/mach-o/parse-initializers32.yaml @@ -0,0 +1,84 @@ +# RUN: lld -flavor darwin -arch i386 -r -print_atoms %s -o %t | FileCheck %s +# +# Test parsing of literal sections. +# + +--- !mach-o +arch: x86 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x89, 0xE5, 0x5D, 0xC3, 0x55, 0x89, 0xE5, + 0x5D, 0xC3, 0x55, 0x89, 0xE5, 0x5D, 0xC3 ] + - segment: __DATA + section: __mod_init_func + type: S_MOD_INIT_FUNC_POINTERS + attributes: [ ] + alignment: 2 + address: 0x0000000000000044 + content: [ 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000000 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: false + extern: false + symbol: 1 + - offset: 0x00000004 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: false + extern: false + symbol: 1 + - segment: __DATA + section: __mod_term_func + type: S_MOD_TERM_FUNC_POINTERS + attributes: [ ] + alignment: 2 + address: 0x0000000000000104 + content: [ 0x0A, 0x00, 0x00, 0x00 ] +global-symbols: + - name: _init + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + - name: _init2 + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000005 + - name: _term + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x000000000000000A +... + + +# CHECK:defined-atoms: +# CHECK: - type: initializer-pointer +# CHECK: content: [ 00, 00, 00, 00 ] +# CHECK: dead-strip: never +# CHECK: - type: initializer-pointer +# CHECK: content: [ 05, 00, 00, 00 ] +# CHECK: dead-strip: never +# CHECK: - type: terminator-pointer +# CHECK: content: [ 0A, 00, 00, 00 ] +# CHECK: dead-strip: never +# CHECK: - name: _init +# CHECK: scope: global +# CHECK: content: [ 55, 89, E5, 5D, C3 ] +# CHECK: - name: _init2 +# CHECK: scope: global +# CHECK: content: [ 55, 89, E5, 5D, C3 ] +# CHECK: - name: _term +# CHECK: scope: global +# CHECK: content: [ 55, 89, E5, 5D, C3 ] diff --git a/test/mach-o/parse-initializers64.yaml b/test/mach-o/parse-initializers64.yaml new file mode 100644 index 0000000000000..5ebd8dafd7ce4 --- /dev/null +++ b/test/mach-o/parse-initializers64.yaml @@ -0,0 +1,105 @@ +# RUN: lld -flavor darwin -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s +# +# Test parsing of literal sections. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x5D, 0xC3, 0x55, 0x48, + 0x89, 0xE5, 0x5D, 0xC3, 0x55, 0x48, 0x89, 0xE5, + 0x5D, 0xC3 ] + - segment: __DATA + section: __mod_init_func + type: S_MOD_INIT_FUNC_POINTERS + attributes: [ ] + alignment: 0 + address: 0x0000000000000100 + content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000000 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 0 + - offset: 0x00000008 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 1 + - segment: __DATA + section: __mod_term_func + type: S_MOD_TERM_FUNC_POINTERS + attributes: [ ] + alignment: 3 + address: 0x0000000000000108 + content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000000 + type: X86_64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 2 +global-symbols: + - name: _init + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + - name: _init2 + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000006 + - name: _term + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x000000000000000C +... + + +# CHECK:defined-atoms: +# CHECK: - type: initializer-pointer +# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ] +# CHECK: dead-strip: never +# CHECK: references: +# CHECK: - kind: pointer64 +# CHECK: offset: 0 +# CHECK: target: _init +# CHECK: - type: initializer-pointer +# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ] +# CHECK: dead-strip: never +# CHECK: references: +# CHECK: - kind: pointer64 +# CHECK: offset: 0 +# CHECK: target: _init2 +# CHECK: - type: terminator-pointer +# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ] +# CHECK: dead-strip: never +# CHECK: references: +# CHECK: - kind: pointer64 +# CHECK: offset: 0 +# CHECK: target: _term +# CHECK: - name: _init +# CHECK: scope: global +# CHECK: content: [ 55, 48, 89, E5, 5D, C3 ] +# CHECK: - name: _init2 +# CHECK: scope: global +# CHECK: content: [ 55, 48, 89, E5, 5D, C3 ] +# CHECK: - name: _term +# CHECK: scope: global +# CHECK: content: [ 55, 48, 89, E5, 5D, C3 ] diff --git a/test/mach-o/parse-literals-error.yaml b/test/mach-o/parse-literals-error.yaml new file mode 100644 index 0000000000000..be21d6efffa3e --- /dev/null +++ b/test/mach-o/parse-literals-error.yaml @@ -0,0 +1,25 @@ +# RUN: not lld -flavor darwin -arch x86_64 -r -print_atoms %s -o %t 2> %t.err +# RUN: FileCheck %s < %t.err +# +# Test for error if literal section is not correct size mulitple. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __literal8 + type: S_8BYTE_LITERALS + attributes: [ ] + alignment: 0 + address: 0x0000000000000120 + content: [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D ] +... + +# CHECK: error: + diff --git a/test/mach-o/parse-literals.yaml b/test/mach-o/parse-literals.yaml new file mode 100644 index 0000000000000..24568ea0fd4fb --- /dev/null +++ b/test/mach-o/parse-literals.yaml @@ -0,0 +1,93 @@ +# RUN: lld -flavor darwin -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s +# +# Test parsing of literal sections. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __cstring + type: S_CSTRING_LITERALS + attributes: [ ] + alignment: 0 + address: 0x0000000000000100 + content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x00, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x00, 0x77, 0x6F, 0x72, 0x6C, + 0x00 ] + - segment: __TEXT + section: __literal4 + type: S_4BYTE_LITERALS + attributes: [ ] + alignment: 0 + address: 0x0000000000000114 + content: [ 0x01, 0x02, 0x03, 0x04, 0x11, 0x12, 0x13, 0x14, + 0x28, 0x29, 0x2A, 0x2B ] + - segment: __TEXT + section: __literal8 + type: S_8BYTE_LITERALS + attributes: [ ] + alignment: 0 + address: 0x0000000000000120 + content: [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F ] + - segment: __TEXT + section: __literal16 + type: S_16BYTE_LITERALS + attributes: [ ] + alignment: 0 + address: 0x0000000000000130 + content: [ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00 ] + - segment: __TEXT + section: __ustring + type: S_REGULAR + attributes: [ ] + alignment: 0 + address: 0x0000000000000100 + content: [ 0x68, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x6C, 0x00, + 0x6F, 0x00, 0x00, 0x00, 0x74, 0x00, 0x68, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x00, 0x00 ] +... + + +# CHECK:defined-atoms: +# CHECK: - scope: hidden +# CHECK: type: c-string +# CHECK: content: [ 68, 65, 6C, 6C, 6F, 00 ] +# CHECK: - scope: hidden +# CHECK: type: c-string +# CHECK: content: [ 74, 68, 65, 72, 65, 00 ] +# CHECK: - scope: hidden +# CHECK: type: c-string +# CHECK: content: [ 77, 6F, 72, 6C, 00 ] +# CHECK: - scope: hidden +# CHECK: type: utf16-string +# CHECK: content: [ 68, 00, 65, 00, 6C, 00, 6C, 00, 6F, 00, 00, 00 ] +# CHECK: - scope: hidden +# CHECK: type: utf16-string +# CHECK: content: [ 74, 00, 68, 00, 65, 00, 72, 00, 00, 00 ] +# CHECK: - scope: hidden +# CHECK: type: const-4-byte +# CHECK: content: [ 01, 02, 03, 04 ] +# CHECK: - scope: hidden +# CHECK: type: const-4-byte +# CHECK: content: [ 11, 12, 13, 14 ] +# CHECK: - scope: hidden +# CHECK: type: const-4-byte +# CHECK: content: [ 28, 29, 2A, 2B ] +# CHECK: - scope: hidden +# CHECK: type: const-8-byte +# CHECK: content: [ 01, 02, 03, 04, 05, 06, 07, 08 ] +# CHECK: - scope: hidden +# CHECK: type: const-8-byte +# CHECK: content: [ 28, 29, 2A, 2B, 2C, 2D, 2E, 2F ] +# CHECK: - scope: hidden +# CHECK: type: const-16-byte +# CHECK: content: [ 01, 02, 03, 04, 05, 06, 07, 08, 09, 0A, 0B, 0C, +# CHECK: 0D, 0E, 0F, 00 ] + diff --git a/test/mach-o/parse-non-lazy-pointers.yaml b/test/mach-o/parse-non-lazy-pointers.yaml new file mode 100644 index 0000000000000..0b0ec5cf36efa --- /dev/null +++ b/test/mach-o/parse-non-lazy-pointers.yaml @@ -0,0 +1,98 @@ +# RUN: lld -flavor darwin -arch i386 -r -print_atoms %s -o %t | FileCheck %s +# +# Test parsing of non-lazy-pointer sections. +# + +--- !mach-o +arch: x86 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x89, 0xE5, 0xE8, 0x00, 0x00, 0x00, 0x00, + 0x59, 0x8D, 0x81, 0x14, 0x00, 0x00, 0x00, 0x8D, + 0x81, 0x18, 0x00, 0x00, 0x00, 0x5D, 0xC3, 0x55, + 0x89, 0xE5, 0x5D, 0xC3 ] + relocations: + - offset: 0x00000011 + scattered: true + type: GENERIC_RELOC_LOCAL_SECTDIFF + length: 2 + pc-rel: false + value: 0x00000020 + - offset: 0x00000000 + scattered: true + type: GENERIC_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x00000008 + - offset: 0x0000000B + scattered: true + type: GENERIC_RELOC_LOCAL_SECTDIFF + length: 2 + pc-rel: false + value: 0x0000001C + - offset: 0x00000000 + scattered: true + type: GENERIC_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x00000008 + - segment: __IMPORT + section: __pointers + type: S_NON_LAZY_SYMBOL_POINTERS + attributes: [ ] + address: 0x000000000000001C + content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] + indirect-syms: [ 2, 2147483648 ] +local-symbols: + - name: _foo + type: N_SECT + sect: 1 + value: 0x0000000000000017 +global-symbols: + - name: _get + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: _bar + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 +... + + +# CHECK:defined-atoms: +# CHECK: - ref-name: [[GOT1:L[L0-9]+]] +# CHECK: scope: hidden +# CHECK: type: got +# CHECK: content: [ 00, 00, 00, 00 ] +# CHECK: merge: by-content +# CHECK: - ref-name: [[GOT2:L[L0-9]+]] +# CHECK: scope: hidden +# CHECK: type: got +# CHECK: content: [ 00, 00, 00, 00 ] +# CHECK: merge: by-content +# CHECK: - name: _get +# CHECK: scope: global +# CHECK: content: [ 55, 89, E5, E8, 00, 00, 00, 00, 59, 8D, 81, 14, +# CHECK: 00, 00, 00, 8D, 81, 18, 00, 00, 00, 5D, C3 ] +# CHECK: references: +# CHECK: - kind: funcRel32 +# CHECK: offset: 11 +# CHECK: target: [[GOT1]] +# CHECK: - kind: funcRel32 +# CHECK: offset: 17 +# CHECK: target: [[GOT2]] +# CHECK: - name: _foo +# CHECK: content: [ 55, 89, E5, 5D, C3 ] + + diff --git a/test/mach-o/parse-relocs-x86.yaml b/test/mach-o/parse-relocs-x86.yaml new file mode 100644 index 0000000000000..3fc22ae71bfc6 --- /dev/null +++ b/test/mach-o/parse-relocs-x86.yaml @@ -0,0 +1,296 @@ +# RUN: lld -flavor darwin -arch i386 -r -print_atoms %s -o %t | FileCheck %s \ +# RUN: && lld -flavor darwin -arch i386 -r -print_atoms %t -o %t2 | FileCheck %s +# +# Test parsing and writing of x86 relocations. +# +# The first step tests if the supplied mach-o file is parsed into the correct +# set of references. The second step verifies relocations can be round-tripped +# by writing to a new .o file, then parsing that file which should result in +# the same references. +# +# .text +#_test: +# call _undef +# call _undef+2 +# call _foo +# call _foo+2 +# callw _undef +# callw _foo +# callw _foo+2 +#L1: +# movl _undef, %eax +# movl _x, %eax +# movl _x+4, %eax +# movl _x-L1(%eax), %eax +# movl _x+4-L1(%eax), %eax +# +#_foo: +# ret +# +# .data +#_x: +# .long _undef +# .long _undef+7 +# .long _foo +# .long _foo+3 +# .long _test - . +# .long _test+3 - . +# + +--- !mach-o +arch: x86 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0xE8, 0xFB, 0xFF, 0xFF, 0xFF, 0xE8, 0xF8, 0xFF, + 0xFF, 0xFF, 0xE8, 0x2C, 0x00, 0x00, 0x00, 0xE8, + 0x29, 0x00, 0x00, 0x00, 0x66, 0xE8, 0xE8, 0xFF, + 0x66, 0xE8, 0x1F, 0x00, 0x66, 0xE8, 0x1D, 0x00, + 0xA1, 0x00, 0x00, 0x00, 0x00, 0xA1, 0x3C, 0x00, + 0x00, 0x00, 0xA1, 0x40, 0x00, 0x00, 0x00, 0x8B, + 0x80, 0x1C, 0x00, 0x00, 0x00, 0x8B, 0x80, 0x20, + 0x00, 0x00, 0x00, 0xC3 ] + relocations: + - offset: 0x00000037 + scattered: true + type: GENERIC_RELOC_LOCAL_SECTDIFF + length: 2 + pc-rel: false + value: 0x0000003C + - offset: 0x00000000 + scattered: true + type: GENERIC_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x00000020 + - offset: 0x00000031 + scattered: true + type: GENERIC_RELOC_LOCAL_SECTDIFF + length: 2 + pc-rel: false + value: 0x0000003C + - offset: 0x00000000 + scattered: true + type: GENERIC_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x00000020 + - offset: 0x0000002B + scattered: true + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: false + value: 0x0000003C + - offset: 0x00000026 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: false + extern: false + symbol: 2 + - offset: 0x00000021 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: false + extern: true + symbol: 3 + - offset: 0x0000001E + scattered: true + type: GENERIC_RELOC_VANILLA + length: 1 + pc-rel: true + value: 0x0000003B + - offset: 0x0000001A + type: GENERIC_RELOC_VANILLA + length: 1 + pc-rel: true + extern: false + symbol: 1 + - offset: 0x00000016 + type: GENERIC_RELOC_VANILLA + length: 1 + pc-rel: true + extern: true + symbol: 3 + - offset: 0x00000010 + scattered: true + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: true + value: 0x0000003B + - offset: 0x0000000B + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: true + extern: false + symbol: 1 + - offset: 0x00000006 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: true + extern: true + symbol: 3 + - offset: 0x00000001 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: true + extern: true + symbol: 3 + - segment: __DATA + section: __data + type: S_REGULAR + attributes: [ ] + address: 0x000000000000003C + content: [ 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3B, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, + 0xB4, 0xFF, 0xFF, 0xFF, 0xB3, 0xFF, 0xFF, 0xFF ] + relocations: + - offset: 0x00000014 + scattered: true + type: GENERIC_RELOC_LOCAL_SECTDIFF + length: 2 + pc-rel: false + value: 0x00000000 + - offset: 0x00000000 + scattered: true + type: GENERIC_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x00000050 + - offset: 0x00000010 + scattered: true + type: GENERIC_RELOC_LOCAL_SECTDIFF + length: 2 + pc-rel: false + value: 0x00000000 + - offset: 0x00000000 + scattered: true + type: GENERIC_RELOC_PAIR + length: 2 + pc-rel: false + value: 0x0000004C + - offset: 0x0000000C + scattered: true + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: false + value: 0x0000003B + - offset: 0x00000008 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: false + extern: false + symbol: 1 + - offset: 0x00000004 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: false + extern: true + symbol: 3 + - offset: 0x00000000 + type: GENERIC_RELOC_VANILLA + length: 2 + pc-rel: false + extern: true + symbol: 3 +local-symbols: + - name: _test + type: N_SECT + sect: 1 + value: 0x0000000000000000 + - name: _foo + type: N_SECT + sect: 1 + value: 0x000000000000003B + - name: _x + type: N_SECT + sect: 2 + value: 0x000000000000003C +undefined-symbols: + - name: _undef + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 +... + +# CHECK: defined-atoms: +# CHECK: - name: _x +# CHECK: type: data +# CHECK: references: +# CHECK: - kind: pointer32 +# CHECK: offset: 0 +# CHECK: target: _undef +# CHECK-NOT: addend: +# CHECK: - kind: pointer32 +# CHECK: offset: 4 +# CHECK: target: _undef +# CHECK: addend: 7 +# CHECK: - kind: pointer32 +# CHECK: offset: 8 +# CHECK: target: _foo +# CHECK-NOT: addend: +# CHECK: - kind: pointer32 +# CHECK: offset: 12 +# CHECK: target: _foo +# CHECK: addend: 3 +# CHECK: - kind: delta32 +# CHECK: offset: 16 +# CHECK: target: _test +# CHECK: - kind: delta32 +# CHECK: offset: 20 +# CHECK: target: _test +# CHECK: addend: 3 +# CHECK: - name: _test +# CHECK: references: +# CHECK: - kind: branch32 +# CHECK: offset: 1 +# CHECK: target: _undef +# CHECK-NOT: addend: +# CHECK: - kind: branch32 +# CHECK: offset: 6 +# CHECK: target: _undef +# CHECK: addend: 2 +# CHECK: - kind: branch32 +# CHECK: offset: 11 +# CHECK: target: _foo +# CHECK-NOT: addend: +# CHECK: - kind: branch32 +# CHECK: offset: 16 +# CHECK: target: _foo +# CHECK: addend: 2 +# CHECK: - kind: branch16 +# CHECK: offset: 22 +# CHECK: target: _undef +# CHECK-NOT: addend: +# CHECK: - kind: branch16 +# CHECK: offset: 26 +# CHECK: target: _foo +# CHECK-NOT: addend: +# CHECK: - kind: branch16 +# CHECK: offset: 30 +# CHECK: target: _foo +# CHECK: addend: 2 +# CHECK: - kind: abs32 +# CHECK: offset: 33 +# CHECK: target: _undef +# CHECK: - kind: abs32 +# CHECK: offset: 38 +# CHECK: target: _x +# CHECK: - kind: abs32 +# CHECK: offset: 43 +# CHECK: target: _x +# CHECK: addend: 4 +# CHECK: - kind: funcRel32 +# CHECK: offset: 49 +# CHECK: target: _x +# CHECK: addend: -32 +# CHECK: - kind: funcRel32 +# CHECK: offset: 55 +# CHECK: target: _x +# CHECK: addend: -28 + diff --git a/test/mach-o/parse-section-no-symbol.yaml b/test/mach-o/parse-section-no-symbol.yaml new file mode 100644 index 0000000000000..46d005a1cd18a --- /dev/null +++ b/test/mach-o/parse-section-no-symbol.yaml @@ -0,0 +1,23 @@ +# RUN: lld -flavor darwin -arch x86_64 -r %s -print_atoms -o %t2 | FileCheck %s +# +# Test parsing of mach-o functions with no symbols at all. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 4 + address: 0x0000000000000000 + content: [ 0xCC ] +... + +# CHECK-NOT: name: +# CHECK: content: [ CC ] diff --git a/test/mach-o/parse-tentative-defs.yaml b/test/mach-o/parse-tentative-defs.yaml new file mode 100644 index 0000000000000..fc75167bd8957 --- /dev/null +++ b/test/mach-o/parse-tentative-defs.yaml @@ -0,0 +1,88 @@ +# RUN: lld -flavor darwin -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s +# +# Test parsing of tentative definitions, including size, scope, and alignment. +# +# +# int tent4; +# long tent8; +# __attribute__((visibility("hidden"))) int tentHidden; +# __attribute__((aligned(16))) int tent4_16; +# __attribute__((aligned(32))) long tent64_32[8]; +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __tex + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS ] + address: 0x0000000000000000 +undefined-symbols: + - name: _tent4 + type: N_UNDF + scope: [ N_EXT ] + desc: 0x0200 + value: 0x0000000000000004 + - name: _tent4_16 + type: N_UNDF + scope: [ N_EXT ] + desc: 0x0400 + value: 0x0000000000000004 + - name: _tent64_32 + type: N_UNDF + scope: [ N_EXT ] + desc: 0x0500 + value: 0x0000000000000040 + - name: _tent8 + type: N_UNDF + scope: [ N_EXT ] + desc: 0x0300 + value: 0x0000000000000008 + - name: _tentHidden + type: N_UNDF + scope: [ N_EXT, N_PEXT ] + desc: 0x0200 + value: 0x0000000000000004 +... + + +# CHECK: defined-atoms: +# CHECK: name: _tent4 +# CHECK: scope: global +# CHECK: type: zero-fill +# CHECK: size: 4 +# CHECK: merge: as-tentative +# CHECK: alignment: 2^2 + +# CHECK: name: _tent4_16 +# CHECK: scope: global +# CHECK: type: zero-fill +# CHECK: size: 4 +# CHECK: merge: as-tentative +# CHECK: alignment: 2^4 + +# CHECK: name: _tent64_32 +# CHECK: scope: global +# CHECK: type: zero-fill +# CHECK: size: 64 +# CHECK: merge: as-tentative +# CHECK: alignment: 2^5 + +# CHECK: name: _tent8 +# CHECK: scope: global +# CHECK: type: zero-fill +# CHECK: size: 8 +# CHECK: merge: as-tentative +# CHECK: alignment: 2^3 + +# CHECK: name: _tentHidden +# CHECK: scope: hidden +# CHECK: type: zero-fill +# CHECK: size: 4 +# CHECK: merge: as-tentative +# CHECK: alignment: 2^2 diff --git a/test/mach-o/parse-text-relocs-arm64.yaml b/test/mach-o/parse-text-relocs-arm64.yaml new file mode 100644 index 0000000000000..38a52e7f6f283 --- /dev/null +++ b/test/mach-o/parse-text-relocs-arm64.yaml @@ -0,0 +1,237 @@ +# RUN: lld -flavor darwin -arch arm64 -r -print_atoms %s -o %t | FileCheck %s \ +# RUN: && lld -flavor darwin -arch arm64 -r -print_atoms %t -o %t2 | FileCheck %s +# +# Test parsing and writing of arm64 text relocations. +# +# The first step tests if the supplied mach-o file is parsed into the correct +# set of references. The second step verifies relocations can be round-tripped +# by writing to a new .o file, then parsing that file which should result in +# the same references. +# +#_test: + + +--- !mach-o +arch: arm64 +file-type: MH_OBJECT +flags: [ ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x94, + 0x01, 0x00, 0x00, 0x90, 0x20, 0x00, 0x40, 0x39, + 0x20, 0x00, 0x40, 0x79, 0x20, 0x00, 0x40, 0xB9, + 0x20, 0x00, 0x40, 0xF9, 0x20, 0x00, 0xC0, 0x3D, + 0x01, 0x00, 0x00, 0x90, 0x20, 0x00, 0x40, 0xB9, + 0x01, 0x00, 0x00, 0x90, 0x20, 0x00, 0x40, 0xF9, + 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x40, 0xF9 ] + relocations: + - offset: 0x00000034 + type: ARM64_RELOC_TLVP_LOAD_PAGEOFF12 + length: 2 + pc-rel: false + extern: true + symbol: 5 + - offset: 0x00000030 + type: ARM64_RELOC_TLVP_LOAD_PAGE21 + length: 2 + pc-rel: true + extern: true + symbol: 5 + - offset: 0x0000002C + type: ARM64_RELOC_GOT_LOAD_PAGEOFF12 + length: 2 + pc-rel: false + extern: true + symbol: 6 + - offset: 0x00000028 + type: ARM64_RELOC_GOT_LOAD_PAGE21 + length: 2 + pc-rel: true + extern: true + symbol: 6 + - offset: 0x00000024 + type: ARM64_RELOC_ADDEND + length: 2 + pc-rel: false + extern: false + symbol: 16 + - offset: 0x00000024 + type: ARM64_RELOC_PAGEOFF12 + length: 2 + pc-rel: false + extern: true + symbol: 2 + - offset: 0x00000020 + type: ARM64_RELOC_ADDEND + length: 2 + pc-rel: false + extern: false + symbol: 16 + - offset: 0x00000020 + type: ARM64_RELOC_PAGE21 + length: 2 + pc-rel: true + extern: true + symbol: 2 + - offset: 0x0000001C + type: ARM64_RELOC_PAGEOFF12 + length: 2 + pc-rel: false + extern: true + symbol: 2 + - offset: 0x00000018 + type: ARM64_RELOC_PAGEOFF12 + length: 2 + pc-rel: false + extern: true + symbol: 2 + - offset: 0x00000014 + type: ARM64_RELOC_PAGEOFF12 + length: 2 + pc-rel: false + extern: true + symbol: 2 + - offset: 0x00000010 + type: ARM64_RELOC_PAGEOFF12 + length: 2 + pc-rel: false + extern: true + symbol: 2 + - offset: 0x0000000C + type: ARM64_RELOC_PAGEOFF12 + length: 2 + pc-rel: false + extern: true + symbol: 2 + - offset: 0x00000008 + type: ARM64_RELOC_PAGE21 + length: 2 + pc-rel: true + extern: true + symbol: 2 + - offset: 0x00000004 + type: ARM64_RELOC_ADDEND + length: 2 + pc-rel: false + extern: false + symbol: 8 + - offset: 0x00000004 + type: ARM64_RELOC_BRANCH26 + length: 2 + pc-rel: true + extern: true + symbol: 4 + - offset: 0x00000000 + type: ARM64_RELOC_BRANCH26 + length: 2 + pc-rel: true + extern: true + symbol: 4 + - segment: __DATA + section: __data + type: S_REGULAR + attributes: [ ] + alignment: 2 + address: 0x0000000000000038 + content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] +local-symbols: + - name: ltmp0 + type: N_SECT + sect: 1 + value: 0x0000000000000000 + - name: _func + type: N_SECT + sect: 1 + value: 0x0000000000000000 + - name: _v1 + type: N_SECT + sect: 2 + value: 0x0000000000000038 + - name: ltmp1 + type: N_SECT + sect: 2 + value: 0x0000000000000038 +undefined-symbols: + - name: _foo + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: _tlv + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: _v2 + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 +... + +# CHECK: defined-atoms: +# CHECK: - name: _v1 +# CHECK: type: data +# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, +# CHECK: 00, 00, 00, 00 ] +# CHECK: - name: _func +# CHECK: content: [ 00, 00, 00, 94, 00, 00, 00, 94, 01, 00, 00, 90, +# CHECK: 20, 00, 40, 39, 20, 00, 40, 79, 20, 00, 40, B9, +# CHECK: 20, 00, 40, F9, 20, 00, C0, 3D, 01, 00, 00, 90, +# CHECK: 20, 00, 40, B9, 01, 00, 00, 90, 20, 00, 40, F9, +# CHECK: 00, 00, 00, 90, 00, 00, 40, F9 ] +# CHECK: references: +# CHECK: - kind: branch26 +# CHECK: offset: 0 +# CHECK: target: _foo +# CHECK: - kind: branch26 +# CHECK: offset: 4 +# CHECK: target: _foo +# CHECK: addend: 8 +# CHECK: - kind: page21 +# CHECK: offset: 8 +# CHECK: target: _v1 +# CHECK: - kind: offset12 +# CHECK: offset: 12 +# CHECK: target: _v1 +# CHECK: - kind: offset12scale2 +# CHECK: offset: 16 +# CHECK: target: _v1 +# CHECK: - kind: offset12scale4 +# CHECK: offset: 20 +# CHECK: target: _v1 +# CHECK: - kind: offset12scale8 +# CHECK: offset: 24 +# CHECK: target: _v1 +# CHECK: - kind: offset12scale16 +# CHECK: offset: 28 +# CHECK: target: _v1 +# CHECK: - kind: page21 +# CHECK: offset: 32 +# CHECK: target: _v1 +# CHECK: addend: 16 +# CHECK: - kind: offset12scale4 +# CHECK: offset: 36 +# CHECK: target: _v1 +# CHECK: addend: 16 +# CHECK: - kind: gotPage21 +# CHECK: offset: 40 +# CHECK: target: _v2 +# CHECK: - kind: gotOffset12 +# CHECK: offset: 44 +# CHECK: target: _v2 +# CHECK: - kind: tlvPage21 +# CHECK: offset: 48 +# CHECK: target: _tlv +# CHECK: - kind: tlvOffset12 +# CHECK: offset: 52 +# CHECK: target: _tlv +# CHECK: undefined-atoms: +# CHECK: - name: _foo +# CHECK: - name: _tlv +# CHECK: - name: _v2 + diff --git a/test/mach-o/parse-text-relocs-x86_64.yaml b/test/mach-o/parse-text-relocs-x86_64.yaml new file mode 100644 index 0000000000000..9e58e02e37717 --- /dev/null +++ b/test/mach-o/parse-text-relocs-x86_64.yaml @@ -0,0 +1,168 @@ +# RUN: lld -flavor darwin -arch x86_64 -r -print_atoms %s -o %t | FileCheck %s \ +# RUN: && lld -flavor darwin -arch x86_64 -r -print_atoms %t -o %t2 | FileCheck %s +# +# Test parsing and writing of x86_64 text relocations. +# +# The first step tests if the supplied mach-o file is parsed into the correct +# set of references. The second step verifies relocations can be round-tripped +# by writing to a new .o file, then parsing that file which should result in +# the same references. +# +#_test: +# call _foo +# call _foo+4 +# movq _foo@GOTPCREL(%rip), %rax +# pushq _foo@GOTPCREL(%rip) +# movl _foo(%rip), %eax +# movl _foo+4(%rip), %eax +# movb $0x12, _foo(%rip) +# movw $0x1234, _foo(%rip) +# movl $0x12345678, _foo(%rip) +# movl L2(%rip), %eax +# +# .data +#L2: .long 0 + + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0xE8, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x04, 0x00, + 0x00, 0x00, 0x48, 0x8B, 0x05, 0x04, 0x00, 0x00, + 0x00, 0xFF, 0x35, 0x04, 0x00, 0x00, 0x00, 0x8B, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x05, 0x04, + 0x00, 0x00, 0x00, 0xC6, 0x05, 0xFF, 0xFF, 0xFF, + 0xFF, 0x12, 0x66, 0xC7, 0x05, 0xFE, 0xFF, 0xFF, + 0xFF, 0x34, 0x12, 0xC7, 0x05, 0xFC, 0xFF, 0xFF, + 0xFF, 0x78, 0x56, 0x34, 0x12, 0x8B, 0x05, 0x00, + 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x0000003F + type: X86_64_RELOC_SIGNED + length: 2 + pc-rel: true + extern: false + symbol: 2 + - offset: 0x00000035 + type: X86_64_RELOC_SIGNED_4 + length: 2 + pc-rel: true + extern: true + symbol: 1 + - offset: 0x0000002D + type: X86_64_RELOC_SIGNED_2 + length: 2 + pc-rel: true + extern: true + symbol: 1 + - offset: 0x00000025 + type: X86_64_RELOC_SIGNED_1 + length: 2 + pc-rel: true + extern: true + symbol: 1 + - offset: 0x0000001F + type: X86_64_RELOC_SIGNED + length: 2 + pc-rel: true + extern: true + symbol: 1 + - offset: 0x00000019 + type: X86_64_RELOC_SIGNED + length: 2 + pc-rel: true + extern: true + symbol: 1 + - offset: 0x00000013 + type: X86_64_RELOC_GOT + length: 2 + pc-rel: true + extern: true + symbol: 1 + - offset: 0x0000000D + type: X86_64_RELOC_GOT_LOAD + length: 2 + pc-rel: true + extern: true + symbol: 1 + - offset: 0x00000006 + type: X86_64_RELOC_BRANCH + length: 2 + pc-rel: true + extern: true + symbol: 1 + - offset: 0x00000001 + type: X86_64_RELOC_BRANCH + length: 2 + pc-rel: true + extern: true + symbol: 1 + - segment: __DATA + section: __data + type: S_REGULAR + attributes: [ ] + address: 0x0000000000000043 + content: [ 0x00, 0x00, 0x00, 0x00 ] +local-symbols: + - name: _test + type: N_SECT + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: _foo + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 +... + +# CHECK: defined-atoms: +# CHECK: - ref-name: [[LABEL:L[0-9]+]] +# CHECK: type: data +# CHECK: content: [ 00, 00, 00, 00 ] +# CHECK: - name: _test +# CHECK: references: +# CHECK: - kind: branch32 +# CHECK: offset: 1 +# CHECK: target: _foo +# CHECK: - kind: branch32 +# CHECK: offset: 6 +# CHECK: target: _foo +# CHECK: addend: 4 +# CHECK: - kind: ripRel32GotLoad +# CHECK: offset: 13 +# CHECK: target: _foo +# CHECK: addend: 4 +# CHECK: - kind: ripRel32Got +# CHECK: offset: 19 +# CHECK: target: _foo +# CHECK: addend: 4 +# CHECK: - kind: ripRel32 +# CHECK: offset: 25 +# CHECK: target: _foo +# CHECK: - kind: ripRel32 +# CHECK: offset: 31 +# CHECK: target: _foo +# CHECK: addend: 4 +# CHECK: - kind: ripRel32Minus1 +# CHECK: offset: 37 +# CHECK: target: _foo +# CHECK-NOT: addend: +# CHECK: - kind: ripRel32Minus2 +# CHECK: offset: 45 +# CHECK: target: _foo +# CHECK-NOT: addend: +# CHECK: - kind: ripRel32Minus4 +# CHECK: offset: 53 +# CHECK: target: _foo +# CHECK-NOT: addend: +# CHECK: - kind: ripRel32Anon +# CHECK: offset: 63 +# CHECK: target: [[LABEL]] +# CHECK-NOT: addend: diff --git a/test/mach-o/re-exported-dylib-ordinal.yaml b/test/mach-o/re-exported-dylib-ordinal.yaml new file mode 100644 index 0000000000000..9d628e9af151f --- /dev/null +++ b/test/mach-o/re-exported-dylib-ordinal.yaml @@ -0,0 +1,105 @@ +# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -dylib -o %t \ +# RUN: && llvm-nm -m %t | FileCheck %s +# +# Test that when one dylib A re-exports dylib B that using a symbol from B +# gets recorded as coming from A. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x31, 0xC0, 0x5D, 0xE9, + 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000008 + type: X86_64_RELOC_BRANCH + length: 2 + pc-rel: true + extern: true + symbol: 1 +global-symbols: + - name: _test + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: _bar + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + +--- !mach-o +arch: x86_64 +file-type: MH_DYLIB +flags: [ MH_TWOLEVEL ] +install-name: /junk/libfoo.dylib +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000F9A + content: [ 0x55, 0x48, 0x89, 0xE5, 0x5D, 0xC3 ] +global-symbols: + - name: _foo + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000F9A +dependents: + - path: /junk/libbar.dylib + kind: LC_REEXPORT_DYLIB + +--- !mach-o +arch: x86_64 +file-type: MH_DYLIB +flags: [ MH_TWOLEVEL ] +install-name: /junk/libbar.dylib +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000F9A + content: [ 0x55, 0x48, 0x89, 0xE5, 0x5D, 0xC3 ] +global-symbols: + - name: _bar + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000F9A + +--- !mach-o +arch: x86_64 +file-type: MH_DYLIB +flags: [ MH_TWOLEVEL ] +install-name: /usr/lib/libSystem.B.dylib +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55 ] + +global-symbols: + - name: dyld_stub_binder + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + +... + +# CHECK: (undefined) external _bar (from libfoo) +# CHECK: (undefined) external dyld_stub_binder (from libSystem) diff --git a/test/mach-o/rpath.yaml b/test/mach-o/rpath.yaml new file mode 100644 index 0000000000000..f0b5f718c15ae --- /dev/null +++ b/test/mach-o/rpath.yaml @@ -0,0 +1,38 @@ +# Check we handle -rpath correctly: +# RUN: lld -flavor darwin -arch x86_64 -rpath @loader_path/../Frameworks \ +# RUN: %p/Inputs/libSystem.yaml %s -o %t +# RUN: llvm-objdump -private-headers %t | FileCheck %s --check-prefix=CHECK-BINARY-WRITE + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 4 + address: 0x0000000000000000 + content: [ 0xCC, 0xC3, 0x90, 0xC3, 0x90, 0x90, 0xC3, 0x90, + 0x90, 0x90, 0xC3, 0x90, 0x90, 0x90, 0x90, 0xC3, + 0x31, 0xC0, 0xC3 ] +local-symbols: + - name: _myStatic + type: N_SECT + sect: 1 + value: 0x000000000000000B +global-symbols: + - name: _main + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000001 +... + + +# CHECK-BINARY-WRITE: cmd LC_RPATH +# CHECK-BINARY-WRITE-NEXT: cmdsize 44 +# CHECK-BINARY-WRITE-NEXT: path @loader_path/../Frameworks (offset 12) diff --git a/test/mach-o/sectalign.yaml b/test/mach-o/sectalign.yaml new file mode 100644 index 0000000000000..3556a5ecbcfcf --- /dev/null +++ b/test/mach-o/sectalign.yaml @@ -0,0 +1,80 @@ +# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -dylib \ +# RUN: -sectalign __DATA __custom 0x800 -sectalign __TEXT __text 0x400 \ +# RUN: %p/Inputs/libSystem.yaml -o %t \ +# RUN: && llvm-readobj -sections %t | FileCheck %s +# +# Test -sectalign option on __text and a custom section. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x8B, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, + 0x5D, 0xC3 ] + relocations: + - offset: 0x0000000C + type: X86_64_RELOC_SIGNED + length: 2 + pc-rel: true + extern: true + symbol: 1 + - offset: 0x00000006 + type: X86_64_RELOC_SIGNED + length: 2 + pc-rel: true + extern: true + symbol: 2 + - segment: __DATA + section: __data + type: S_REGULAR + attributes: [ ] + alignment: 2 + address: 0x0000000000000014 + content: [ 0x0A, 0x00, 0x00, 0x00 ] + - segment: __DATA + section: __custom + type: S_REGULAR + attributes: [ ] + alignment: 2 + address: 0x0000000000000018 + content: [ 0x0A, 0x00, 0x00, 0x00 ] +global-symbols: + - name: _a + type: N_SECT + scope: [ N_EXT ] + sect: 2 + value: 0x0000000000000014 + - name: _b + type: N_SECT + scope: [ N_EXT ] + sect: 3 + value: 0x0000000000000018 + - name: _get + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + +... + + +# CHECK: Name: __text (5F 5F 74 65 78 74 00 00 00 00 00 00 00 00 00 00) +# CHECK: Segment: __TEXT (5F 5F 54 45 58 54 00 00 00 00 00 00 00 00 00 00) +# CHECK: Address: 0xC00 + +# CHECK: Name: __data (5F 5F 64 61 74 61 00 00 00 00 00 00 00 00 00 00) +# CHECK: Segment: __DATA (5F 5F 44 41 54 41 00 00 00 00 00 00 00 00 00 00) +# CHECK: Address: 0x1000 + +# CHECK: Name: __custom (5F 5F 63 75 73 74 6F 6D 00 00 00 00 00 00 00 00) +# CHECK: Segment: __DATA (5F 5F 44 41 54 41 00 00 00 00 00 00 00 00 00 00) +# CHECK: Address: 0x1800 + diff --git a/test/mach-o/unwind-info-simple-arm64.yaml b/test/mach-o/unwind-info-simple-arm64.yaml new file mode 100644 index 0000000000000..d46b43ff712d4 --- /dev/null +++ b/test/mach-o/unwind-info-simple-arm64.yaml @@ -0,0 +1,280 @@ +# RUN: lld -flavor darwin -arch arm64 %s -o %t -e _main %p/Inputs/libSystem.yaml +# RUN: llvm-objdump -unwind-info %t | FileCheck %s + +--- !mach-o +arch: arm64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 2 + address: 0x0000000000000000 + content: [ 0xFD, 0x7B, 0xBF, 0xA9, 0xFD, 0x03, 0x00, 0x91, + 0xE0, 0x03, 0x1E, 0x32, 0x00, 0x00, 0x00, 0x94, + 0x48, 0x01, 0x80, 0x52, 0x08, 0x00, 0x00, 0xB9, + 0x02, 0x00, 0x80, 0xD2, 0x01, 0x00, 0x00, 0x90, + 0x21, 0x00, 0x40, 0xF9, 0x00, 0x00, 0x00, 0x94, + 0xFD, 0x7B, 0xBF, 0xA9, 0xFD, 0x03, 0x00, 0x91, + 0xE0, 0x03, 0x1E, 0x32, 0x00, 0x00, 0x00, 0x94, + 0x48, 0x01, 0x80, 0x52, 0x08, 0x00, 0x00, 0xB9, + 0x02, 0x00, 0x80, 0xD2, 0x01, 0x00, 0x00, 0x90, + 0x21, 0x00, 0x40, 0xF9, 0x00, 0x00, 0x00, 0x94, + 0x3F, 0x04, 0x00, 0x71, 0x81, 0x00, 0x00, 0x54, + 0x00, 0x00, 0x00, 0x94, 0xFD, 0x7B, 0xC1, 0xA8, + 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x94, + 0xFD, 0x7B, 0xBF, 0xA9, 0xFD, 0x03, 0x00, 0x91, + 0x00, 0x00, 0x00, 0x94 ] + relocations: + - offset: 0x00000070 + type: ARM64_RELOC_BRANCH26 + length: 2 + pc-rel: true + extern: true + symbol: 5 + - offset: 0x00000064 + type: ARM64_RELOC_BRANCH26 + length: 2 + pc-rel: true + extern: true + symbol: 7 + - offset: 0x00000060 + type: ARM64_RELOC_BRANCH26 + length: 2 + pc-rel: true + extern: true + symbol: 12 + - offset: 0x00000058 + type: ARM64_RELOC_BRANCH26 + length: 2 + pc-rel: true + extern: true + symbol: 11 + - offset: 0x0000004C + type: ARM64_RELOC_BRANCH26 + length: 2 + pc-rel: true + extern: true + symbol: 13 + - offset: 0x00000048 + type: ARM64_RELOC_GOT_LOAD_PAGEOFF12 + length: 2 + pc-rel: false + extern: true + symbol: 8 + - offset: 0x00000044 + type: ARM64_RELOC_GOT_LOAD_PAGE21 + length: 2 + pc-rel: true + extern: true + symbol: 8 + - offset: 0x00000034 + type: ARM64_RELOC_BRANCH26 + length: 2 + pc-rel: true + extern: true + symbol: 10 + - offset: 0x00000024 + type: ARM64_RELOC_BRANCH26 + length: 2 + pc-rel: true + extern: true + symbol: 13 + - offset: 0x00000020 + type: ARM64_RELOC_GOT_LOAD_PAGEOFF12 + length: 2 + pc-rel: false + extern: true + symbol: 8 + - offset: 0x0000001C + type: ARM64_RELOC_GOT_LOAD_PAGE21 + length: 2 + pc-rel: true + extern: true + symbol: 8 + - offset: 0x0000000C + type: ARM64_RELOC_BRANCH26 + length: 2 + pc-rel: true + extern: true + symbol: 10 + - segment: __TEXT + section: __gcc_except_tab + type: S_REGULAR + attributes: [ ] + alignment: 2 + address: 0x0000000000000074 + content: [ 0xFF, 0x9B, 0xAF, 0x80, 0x00, 0x03, 0x27, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x01, 0x28, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0xD0, 0xFF, 0xFF, 0xFF ] + relocations: + - offset: 0x00000030 + type: ARM64_RELOC_POINTER_TO_GOT + length: 2 + pc-rel: true + extern: true + symbol: 9 + - segment: __LD + section: __compact_unwind + type: S_REGULAR + attributes: [ ] + alignment: 3 + address: 0x00000000000000A8 + content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000040 + type: ARM64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: false + symbol: 1 + - offset: 0x00000038 + type: ARM64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: false + symbol: 2 + - offset: 0x00000030 + type: ARM64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: true + symbol: 14 + - offset: 0x00000020 + type: ARM64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: false + symbol: 1 + - offset: 0x00000000 + type: ARM64_RELOC_UNSIGNED + length: 3 + pc-rel: false + extern: false + symbol: 1 +local-symbols: + - name: ltmp0 + type: N_SECT + sect: 1 + value: 0x0000000000000000 + - name: ltmp14 + type: N_SECT + sect: 2 + value: 0x0000000000000074 + - name: GCC_except_table1 + type: N_SECT + sect: 2 + value: 0x0000000000000074 + - name: ltmp21 + type: N_SECT + sect: 3 + value: 0x00000000000000A8 +global-symbols: + - name: __Z3barv + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000028 + - name: __Z3foov + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + - name: _main + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000068 +undefined-symbols: + - name: __Unwind_Resume + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: __ZTIi + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: __ZTIl + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: ___cxa_allocate_exception + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: ___cxa_begin_catch + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: ___cxa_end_catch + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: ___cxa_throw + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: ___gxx_personality_v0 + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + +--- !mach-o +arch: arm64 +file-type: MH_DYLIB +install-name: /usr/lib/libc++.dylib +exports: + - name: __Unwind_Resume + - name: __ZTIl + - name: __ZTIi + - name: ___cxa_end_catch + - name: ___cxa_begin_catch + - name: ___cxa_allocate_exception + - name: ___cxa_throw + - name: ___gxx_personality_v0 + +... + + +# CHECK: Contents of __unwind_info section: +# CHECK: Version: 0x1 +# CHECK: Common encodings array section offset: 0x1c +# CHECK: Number of common encodings in array: 0x0 +# CHECK: Personality function array section offset: 0x1c +# CHECK: Number of personality functions in array: 0x1 +# CHECK: Index array section offset: 0x20 +# CHECK: Number of indices in array: 0x2 +# CHECK: Common encodings: (count = 0) +# CHECK: Personality functions: (count = 1) +# CHECK: personality[1]: 0x00004018 +# CHECK: Top level indices: (count = 2) +# CHECK: [0]: function offset=0x00003e68, 2nd level page offset=0x00000040, LSDA offset=0x00000038 +# CHECK: [1]: function offset=0x00003edc, 2nd level page offset=0x00000000, LSDA offset=0x00000040 +# CHECK: LSDA descriptors: +# CHECK: [0]: function offset=0x00003e90, LSDA offset=0x00003f6c +# CHECK: Second level indices: +# CHECK: Second level index[0]: offset in section=0x00000040, base function offset=0x00003e68 +# CHECK: [0]: function offset=0x00003e68, encoding=0x04000000 +# CHECK: [1]: function offset=0x00003e90, encoding=0x54000000 +# CHECK: [2]: function offset=0x00003ed0, encoding=0x04000000 +# CHECK-NOT: Contents of __compact_unwind section + + + diff --git a/test/mach-o/unwind-info-simple-x86_64.yaml b/test/mach-o/unwind-info-simple-x86_64.yaml new file mode 100644 index 0000000000000..8886e52716610 --- /dev/null +++ b/test/mach-o/unwind-info-simple-x86_64.yaml @@ -0,0 +1,118 @@ +# RUN: lld -flavor darwin -arch x86_64 %s -o %t -e _main %p/Inputs/libSystem.yaml +# RUN: llvm-objdump -unwind-info %t | FileCheck %s + +# CHECK: Contents of __unwind_info section: +# CHECK: Version: 0x1 +# CHECK: Common encodings array section offset: 0x1c +# CHECK: Number of common encodings in array: 0x0 +# CHECK: Personality function array section offset: 0x1c +# CHECK: Number of personality functions in array: 0x1 +# CHECK: Index array section offset: 0x20 +# CHECK: Number of indices in array: 0x2 +# CHECK: Common encodings: (count = 0) +# CHECK: Personality functions: (count = 1) +# CHECK: personality[1]: 0x00001000 +# CHECK: Top level indices: (count = 2) +# CHECK: [0]: function offset=0x00000efb, 2nd level page offset=0x00000040, LSDA offset=0x00000038 +# CHECK: [1]: function offset=0x00000f00, 2nd level page offset=0x00000000, LSDA offset=0x00000040 +# CHECK: LSDA descriptors: +# CHECK: [0]: function offset=0x00000efb, LSDA offset=0x00000f00 +# CHECK: Second level indices: +# CHECK: Second level index[0]: offset in section=0x00000040, base function offset=0x00000efb +# CHECK: [0]: function offset=0x00000efb, encoding=0x51000000 +# CHECK: [1]: function offset=0x00000efc, encoding=0x01000000 +# CHECK: [2]: function offset=0x00000efd, encoding=0x04000018 +# CHECK: [3]: function offset=0x00000efe, encoding=0x04000040 +# CHECK: [4]: function offset=0x00000eff, encoding=0x00000000 +# CHECK-NOT: Contents of __compact_unwind section + +--- !native +path: '<linker-internal>' +defined-atoms: + - name: GCC_except_table1 + type: unwind-lsda + content: [ FF, 9B, A2, 80, 80, 00, 03, 1A, 08, 00, 00, 00, + 05, 00, 00, 00, 1A, 00, 00, 00, 01, 0D, 00, 00, + 00, 64, 00, 00, 00, 00, 00, 00, 00, 00, 01, 00, + 04, 00, 00, 00 ] + - type: compact-unwind + content: [ 40, 00, 00, 00, 00, 00, 00, 00, 01, 00, 00, 00, + 00, 00, 00, 41, 00, 00, 00, 00, 00, 00, 00, 00, + E0, 00, 00, 00, 00, 00, 00, 00 ] + references: + - kind: pointer64Anon + offset: 0 + target: __Z3barv + - kind: pointer64 + offset: 16 + target: ___gxx_personality_v0 + - kind: pointer64Anon + offset: 24 + target: GCC_except_table1 + - type: compact-unwind + content: [ C0, 00, 00, 00, 00, 00, 00, 00, 01, 00, 00, 00, + 00, 00, 00, 01, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00 ] + references: + - kind: pointer64Anon + offset: 0 + target: _main + - type: compact-unwind + content: [ C1, 00, 00, 00, 00, 00, 00, 00, 01, 00, 00, 00, + 00, 00, 00, 04, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00 ] + references: + - kind: pointer64Anon + offset: 0 + target: _needsDwarfButNoCompactUnwind + +# Generic x86_64 CIE: + - type: unwind-cfi + content: [ 14, 00, 00, 00, 00, 00, 00, 00, 01, 7A, 52, 00, + 01, 78, 10, 01, 10, 0C, 07, 08, 90, 01, 00, 00 ] + + - type: unwind-cfi + content: [ 24, 00, 00, 00, 1C, 00, 00, 00, C8, FE, FF, FF, + FF, FF, FF, FF, 01, 00, 00, 00, 00, 00, 00, 00, + 00, 41, 0E, 10, 86, 02, 43, 0D, 06, 00, 00, 00, + 00, 00, 00, 00 ] + references: + - kind: unwindFDEToFunction + offset: 8 + target: _needsDwarfButNoCompactUnwind + + - type: unwind-cfi + content: [ 24, 00, 00, 00, 44, 00, 00, 00, C8, FE, FF, FF, + FF, FF, FF, FF, 01, 00, 00, 00, 00, 00, 00, 00, + 00, 41, 0E, 10, 86, 02, 43, 0D, 06, 00, 00, 00, + 00, 00, 00, 00 ] + references: + - kind: unwindFDEToFunction + offset: 8 + target: _needsDwarfSaysCompactUnwind + + + - name: __Z3barv + scope: global + content: [ C3 ] + - name: _main + scope: global + content: [ C3 ] + references: + - kind: branch32 + offset: 9 + target: __Z3barv + - name: _needsDwarfButNoCompactUnwind + scope: global + content: [ C3 ] + - name: _needsDwarfSaysCompactUnwind + scope: global + content: [ C3 ] + - name: _noUnwindData + scope: global + content: [ C3 ] + +shared-library-atoms: + - name: ___gxx_personality_v0 + load-name: '/usr/lib/libc++abi.dylib' + type: unknown diff --git a/test/mach-o/upward-dylib-load-command.yaml b/test/mach-o/upward-dylib-load-command.yaml new file mode 100644 index 0000000000000..fee3e41d5bd5f --- /dev/null +++ b/test/mach-o/upward-dylib-load-command.yaml @@ -0,0 +1,48 @@ +# RUN: lld -flavor darwin -arch x86_64 -dylib %p/Inputs/bar.yaml \ +# RUN: -install_name /usr/lib/libbar.dylib %p/Inputs/libSystem.yaml -o %t1.dylib +# RUN: lld -flavor darwin -arch x86_64 -dylib %s -upward_library %t1.dylib \ +# RUN: -install_name /usr/lib/libfoo.dylib %p/Inputs/libSystem.yaml -o %t +# RUN: llvm-objdump -private-headers %t | FileCheck %s +# +# +# Test upward linking: 1) build libbar.dylib, 2) build libfoo.dylib and upward +# like with libbar.dylib, 3) dump load commands of libfoo and verify upward link. +# + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0x31, 0xC0, 0x5D, 0xE9, + 0x00, 0x00, 0x00, 0x00 ] + relocations: + - offset: 0x00000008 + type: X86_64_RELOC_BRANCH + length: 2 + pc-rel: true + extern: true + symbol: 1 +global-symbols: + - name: _foo + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: _bar + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + +... + + +# CHECK: cmd LC_LOAD_UPWARD_DYLIB +# CHECK-NEXT: cmdsize 48 +# CHECK-NEXT: name /usr/lib/libbar.dylib (offset 24) diff --git a/test/mach-o/upward-dylib-paths.yaml b/test/mach-o/upward-dylib-paths.yaml new file mode 100644 index 0000000000000..53ff9fa2298cd --- /dev/null +++ b/test/mach-o/upward-dylib-paths.yaml @@ -0,0 +1,18 @@ +# +# +# RUN: lld -flavor darwin -arch x86_64 -r -test_file_usage -v \ +# RUN: -path_exists /Custom/Frameworks \ +# RUN: -path_exists /Custom/Frameworks/Bar.framework/Bar \ +# RUN: -path_exists /usr/lib \ +# RUN: -path_exists /usr/lib/libfoo.dylib \ +# RUN: -path_exists /opt/stuff/libstuff.dylib \ +# RUN: -F/Custom/Frameworks \ +# RUN: -upward_framework Bar \ +# RUN: -upward-lfoo \ +# RUN: -upward_library /opt/stuff/libstuff.dylib \ +# RUN: 2>&1 | FileCheck %s + +# CHECK: Found upward framework /Custom/Frameworks/Bar.framework/Bar +# CHECK: Found upward library /usr/lib/libfoo.dylib + + diff --git a/test/mach-o/usage.yaml b/test/mach-o/usage.yaml new file mode 100644 index 0000000000000..20a506284c965 --- /dev/null +++ b/test/mach-o/usage.yaml @@ -0,0 +1,8 @@ +# RUN: not lld -flavor darwin | FileCheck %s +# +# Test that running darwin linker with no option prints out usage message. +# + + +# CHECK: USAGE: +# CHECK: -arch diff --git a/test/mach-o/use-simple-dylib.yaml b/test/mach-o/use-simple-dylib.yaml new file mode 100644 index 0000000000000..0da7d1b0bd05f --- /dev/null +++ b/test/mach-o/use-simple-dylib.yaml @@ -0,0 +1,131 @@ +# RUN: lld -flavor darwin -arch x86_64 -print_atoms -r %s -o %t | FileCheck %s + + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55, 0x48, 0x89, 0xE5, 0xE8, 0x00, 0x00, 0x00, + 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x00, + 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, + 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xE9, 0x00, + 0x00, 0x00, 0x00 ] +global-symbols: + - name: _foo + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +undefined-symbols: + - name: _myGlobal + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: _myGlobalWeak + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: _myHidden + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: _myHiddenWeak + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: _myResolver + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: _myStatic + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + - name: _myVariablePreviouslyKnownAsPrivateExtern + type: N_UNDF + scope: [ N_EXT ] + value: 0x0000000000000000 + +--- !mach-o +arch: x86_64 +file-type: MH_DYLIB +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 4 + address: 0x0000000000000000 + content: [ 0xCC, 0xC3, 0x90, 0xC3, 0x90, 0x90, 0xC3, 0x90, + 0x90, 0x90, 0xC3, 0x90, 0x90, 0x90, 0x90, 0xC3, + 0x31, 0xC0, 0xC3 ] +local-symbols: + - name: _myStatic + type: N_SECT + sect: 1 + value: 0x000000000000000B + - name: _myVariablePreviouslyKnownAsPrivateExtern + type: N_SECT + scope: [ N_PEXT ] + sect: 1 + desc: [ N_SYMBOL_RESOLVER ] + value: 0x0000000000000011 +global-symbols: + - name: _myGlobal + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000001 + - name: _myGlobalWeak + type: N_SECT + scope: [ N_EXT ] + sect: 1 + desc: [ N_WEAK_DEF ] + value: 0x0000000000000002 + - name: _myHidden + type: N_SECT + scope: [ N_EXT, N_PEXT ] + sect: 1 + value: 0x0000000000000004 + - name: _myHiddenWeak + type: N_SECT + scope: [ N_EXT, N_PEXT ] + sect: 1 + desc: [ N_WEAK_DEF ] + value: 0x0000000000000007 + - name: _myResolver + type: N_SECT + scope: [ N_EXT ] + sect: 1 + desc: [ N_SYMBOL_RESOLVER ] + value: 0x0000000000000010 + +install-name: libspecial.dylib +... + + +# CHECK: undefined-atoms: +# CHECK: - name: _myStatic +# CHECK: - name: _myVariablePreviouslyKnownAsPrivateExtern +# CHECK: shared-library-atoms: +# CHECK: - name: _myGlobal +# CHECK: load-name: libspecial.dylib +# CHECK: - name: _myGlobalWeak +# CHECK: load-name: libspecial.dylib +# CHECK: - name: _myHidden +# CHECK: load-name: libspecial.dylib +# CHECK: - name: _myHiddenWeak +# CHECK: load-name: libspecial.dylib +# CHECK: - name: _myResolver +# CHECK: load-name: libspecial.dylib diff --git a/test/mach-o/write-final-sections.yaml b/test/mach-o/write-final-sections.yaml new file mode 100644 index 0000000000000..7d4afb31900a4 --- /dev/null +++ b/test/mach-o/write-final-sections.yaml @@ -0,0 +1,167 @@ +# RUN: lld -flavor darwin -arch x86_64 %s -o %t -e _foo +# RUN: llvm-readobj -sections -section-data %t | FileCheck %s + +--- !native +defined-atoms: +# For __TEXT, __text (with typeCode) + - name: _foo + scope: global + content: [ 55 ] +# CHECK: Name: __text +# CHECK: Segment: __TEXT +# CHECK: SectionData ( +# CHECK-NEXT: 0000: 55 +# CHECK-NEXT: ) + +# For __TEXT, __const (with typeConstant), + - type: constant + content: [ 01, 00, 00, 00 ] +# From __TEXT, __literal4, (with typeLiteral4) + - scope: hidden + type: const-4-byte + content: [ 02, 00, 00, 00 ] +# From __TEXT, __literal8, (with typeLiteral8) + - scope: hidden + type: const-8-byte + content: [ 03, 00, 00, 00, 00, 00, 00, 00 ] +# From __TEXT, __literal16, (with typeLiteral16) + - scope: hidden + type: const-16-byte + content: [ 04, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 ] +# CHECK: Name: __const +# CHECK: Segment: __TEXT +# CHECK: SectionData ( +# CHECK-NEXT: 0000: 01000000 02000000 03000000 00000000 +# CHECK-NEXT: 0010: 04000000 00000000 00000000 00000000 +# CHECK-NEXT: ) + +# For __TEXT, __cstring (with typeCString) + - scope: hidden + type: c-string + content: [ 57, 69, 62, 62, 6C, 65, 00 ] + merge: by-content +# CHECK: Name: __cstring +# CHECK: Segment: __TEXT +# CHECK: SectionData ( +# CHECK-NEXT: 0000: 57696262 6C6500 +# CHECK-NEXT: ) + +# For __TEXT, __ustring (with typeUTF16String) + - scope: hidden + type: utf16-string + content: [ 05, 00 ] + merge: by-content +# CHECK: Name: __ustring +# CHECK: Segment: __TEXT +# CHECK: SectionData ( +# CHECK-NEXT: 0000: 0500 +# CHECK-NEXT: ) + +# For __TEXT, __gcc_except_tab, (with typeLSDA) + - name: GCC_except_table0 + type: unwind-lsda + content: [ 06, 00 ] +# CHECK: Name: __gcc_except_tab +# CHECK: Segment: __TEXT +# CHECK: SectionData ( +# CHECK-NEXT: 0000: 0600 +# CHECK-NEXT: ) + +# For __TEXT, __eh_frame, (with typeCFI) + - type: unwind-cfi + content: [ 07 ] +# CHECK: Name: __eh_frame +# CHECK: Segment: __TEXT +# CHECK: SectionData ( +# CHECK-NEXT: 0000: 07 +# CHECK-NEXT: ) + +# For __DATA, __data, (with typeData) + - name: var + type: data + content: [ 08 ] +# CHECK: Name: __data +# CHECK: Segment: __DATA +# CHECK: SectionData ( +# CHECK-NEXT: 0000: 08 +# CHECK-NEXT: ) + +# For __DATA, __bss (with typeZeroFill) +# FIXME: Attributes & tags of __bss are mostly broken. Should be at end of +# __DATA, should have size, should have S_ZEROFILL flag. + - type: zero-fill + size: 8 +# CHECK: Name: __bss +# CHECK: Segment: __DATA + +# For __DATA, __const, (with typeConstData) + - type: const-data + content: [ 09, 00, 00, 00 ] +# CHECK: Name: __const +# CHECK: Segment: __DATA +# CHECK: SectionData ( +# CHECK-NEXT: 0000: 09000000 +# CHECK-NEXT: ) + +# For __DATA, __cfstring, (with typeCFString) + - type: cfstring + content: [ 0A, 00 ] +# CHECK: Name: __cfstring +# CHECK: Segment: __DATA +# CHECK: SectionData ( +# CHECK-NEXT: 0000: 0A00 +# CHECK-NEXT: ) + +# For __DATA, __got (with typeGOT) + - type: got + content: [ 0B, 00, 00, 00, 00, 00, 00, 00 ] +# CHECK: Name: __got +# CHECK: Segment: __DATA +# CHECK: SectionData ( +# CHECK-NEXT: 0000: 0B000000 00000000 +# CHECK-NEXT: ) + + +# For __DATA, __mod_init_func (with typeInitializerPtr) + - type: initializer-pointer + content: [ 0C, 00, 00, 00, 00, 00, 00, 00 ] +# CHECK: Name: __mod_init_func +# CHECK: Segment: __DATA +# CHECK: SectionData ( +# CHECK-NEXT: 0000: 0C000000 00000000 +# CHECK-NEXT: ) + +# For __DATA, __mod_term_func (with typeTerminatorPointer) + - type: terminator-pointer + content: [ 0D, 00, 00, 00, 00, 00, 00, 00 ] +# CHECK: Name: __mod_term_func +# CHECK: Segment: __DATA +# CHECK: SectionData ( +# CHECK-NEXT: 0000: 0D000000 00000000 +# CHECK-NEXT: ) + + - type: compact-unwind + content: [ 0E, 00, 00, 00, 00, 00, 00, 00 ] +# CHECK-NOT: Name: __compact_unwind + + +--- !mach-o +arch: x86_64 +file-type: MH_DYLIB +flags: [ ] +install-name: /usr/lib/libSystem.B.dylib +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0x55 ] + +global-symbols: + - name: dyld_stub_binder + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + diff --git a/test/mach-o/wrong-arch-error.yaml b/test/mach-o/wrong-arch-error.yaml new file mode 100644 index 0000000000000..6d233798a7b6c --- /dev/null +++ b/test/mach-o/wrong-arch-error.yaml @@ -0,0 +1,49 @@ +# RUN: not lld -flavor darwin -arch x86_64 -r %s 2> %t.err +# RUN: FileCheck %s < %t.err + +--- !mach-o +arch: x86_64 +file-type: MH_OBJECT +flags: [ ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS ] + address: 0x0000000000000000 + content: [ 0xCC ] + +global-symbols: + - name: _foo + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 + +--- !mach-o +arch: x86 +file-type: MH_OBJECT +flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ] +has-UUID: false +OS: unknown +sections: + - segment: __TEXT + section: __text + type: S_REGULAR + attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ] + alignment: 4 + address: 0x0000000000000000 + content: [ 0xC3 ] + +global-symbols: + - name: _bar + type: N_SECT + scope: [ N_EXT ] + sect: 1 + value: 0x0000000000000000 +... + + +# CHECK: wrong architecture diff --git a/test/pecoff/Inputs/abs.obj.yaml b/test/pecoff/Inputs/abs.obj.yaml new file mode 100644 index 0000000000000..65c3901ad8e74 --- /dev/null +++ b/test/pecoff/Inputs/abs.obj.yaml @@ -0,0 +1,11 @@ +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [] +sections: +symbols: + - Name: _abs_value + Value: 0xDEADBEEF + SectionNumber: -1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL diff --git a/test/pecoff/Inputs/alignment.obj.yaml b/test/pecoff/Inputs/alignment.obj.yaml new file mode 100644 index 0000000000000..1b12da0e6badf --- /dev/null +++ b/test/pecoff/Inputs/alignment.obj.yaml @@ -0,0 +1,103 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 256 + SectionData: CC + - Name: .text$1 + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4096 + SectionData: 00 + - Name: .data$1 + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: 00 + - Name: .data$2 + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: 11 + - Name: .data$3 + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 0 + SectionData: 22 + - Name: .bss$1 + Characteristics: [ IMAGE_SCN_CNT_UNINITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 16 + SectionData: 0000 + - Name: .bss$2 + Characteristics: [ IMAGE_SCN_CNT_UNINITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 16 + SectionData: 0000 + - Name: .yyy + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4096 + SectionData: 0000 + - Name: .zzz + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 16384 + SectionData: 0000 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .text$1 + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .data$1 + Value: 0 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .data$2 + Value: 0 + SectionNumber: 4 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .data$3 + Value: 0 + SectionNumber: 5 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: _start + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: .bss$1 + Value: 0 + SectionNumber: 6 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .bss$2 + Value: 0 + SectionNumber: 7 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .foo + Value: 0 + SectionNumber: 8 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .bar + Value: 0 + SectionNumber: 9 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC +... diff --git a/test/pecoff/Inputs/alternatename1.obj.yaml b/test/pecoff/Inputs/alternatename1.obj.yaml new file mode 100644 index 0000000000000..dac18a8ef5955 --- /dev/null +++ b/test/pecoff/Inputs/alternatename1.obj.yaml @@ -0,0 +1,23 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 1 + SectionData: 90909090 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: _foo + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/alternatename2.obj.yaml b/test/pecoff/Inputs/alternatename2.obj.yaml new file mode 100644 index 0000000000000..5b70bfced6a1e --- /dev/null +++ b/test/pecoff/Inputs/alternatename2.obj.yaml @@ -0,0 +1,23 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: CCCCCCCC +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: _main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/alternatename3.obj.yaml b/test/pecoff/Inputs/alternatename3.obj.yaml new file mode 100644 index 0000000000000..1865653ea6684 --- /dev/null +++ b/test/pecoff/Inputs/alternatename3.obj.yaml @@ -0,0 +1,39 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 1 + SectionData: 90909090 + - Name: .drectve + Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ] + Alignment: 2147483648 + SectionData: 2F616C7465726E6174656E616D653A5F6D61696E3D5F666F6F00 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: _foo + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: .drectve + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 13 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 +... diff --git a/test/pecoff/Inputs/armnt-ImageBase.obj.yaml b/test/pecoff/Inputs/armnt-ImageBase.obj.yaml new file mode 100644 index 0000000000000..69cd530253b0c --- /dev/null +++ b/test/pecoff/Inputs/armnt-ImageBase.obj.yaml @@ -0,0 +1,39 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_ARMNT + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_PURGEABLE, IMAGE_SCN_MEM_16BIT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 7047FEDE00000000 + Relocations: + - VirtualAddress: 4 + SymbolName: __ImageBase + Type: 1 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 8 + NumberOfRelocations: 1 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: mainCRTStartup + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: __ImageBase + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/armnt-ImageBase.s b/test/pecoff/Inputs/armnt-ImageBase.s new file mode 100644 index 0000000000000..a17458eddb15b --- /dev/null +++ b/test/pecoff/Inputs/armnt-ImageBase.s @@ -0,0 +1,16 @@ + + .syntax unified + .thumb + .text + + .def mainCRTStartup + .type 32 + .scl 2 + .endef + .align 2 + .thumb_func +mainCRTStartup: + bx lr + trap + .long __ImageBase + diff --git a/test/pecoff/Inputs/armnt-addr32-exec.obj.yaml b/test/pecoff/Inputs/armnt-addr32-exec.obj.yaml new file mode 100644 index 0000000000000..b550f58c03e62 --- /dev/null +++ b/test/pecoff/Inputs/armnt-addr32-exec.obj.yaml @@ -0,0 +1,55 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_ARMNT + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_PURGEABLE, IMAGE_SCN_MEM_16BIT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: '7047' + - Name: .rdata + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: '00000000' + Relocations: + - VirtualAddress: 0 + SymbolName: function + Type: 1 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 2 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: .rdata + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 4 + NumberOfRelocations: 1 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 2 + - Name: function + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: fps + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/armnt-addr32-exec.s b/test/pecoff/Inputs/armnt-addr32-exec.s new file mode 100644 index 0000000000000..6a0776de7dd38 --- /dev/null +++ b/test/pecoff/Inputs/armnt-addr32-exec.s @@ -0,0 +1,24 @@ + +# __declspec(dllexport) void function(void) { } +# const void * const fps[] = { &function, }; + + .syntax unified + .thumb + .text + + .def function + .scl 2 + .type 32 + .endef + .global function + .align 2 + .thumb_func +function: + bx lr + + .section .rdata,"rd" + .global fps + .align 2 +fps: + .long function + diff --git a/test/pecoff/Inputs/armnt-addr32.obj.yaml b/test/pecoff/Inputs/armnt-addr32.obj.yaml new file mode 100644 index 0000000000000..62ae2c6ea9920 --- /dev/null +++ b/test/pecoff/Inputs/armnt-addr32.obj.yaml @@ -0,0 +1,39 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_ARMNT + Characteristics: [ ] +sections: + - Name: .rdata + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: '0000000000000000' + Relocations: + - VirtualAddress: 0 + SymbolName: i + Type: 1 +symbols: + - Name: .rdata + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 8 + NumberOfRelocations: 1 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: i + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: is + Value: 4 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/armnt-addr32.s b/test/pecoff/Inputs/armnt-addr32.s new file mode 100644 index 0000000000000..c718e9518af99 --- /dev/null +++ b/test/pecoff/Inputs/armnt-addr32.s @@ -0,0 +1,18 @@ + +@ static const int i = 0; +@ const int * const is[] = { &i, }; + + .syntax unified + .thumb + .text + + .section .rdata,"rd" + .align 2 # @i +i: + .long 0 # 0x0 + + .global is # @is + .align 2 +is: + .long i + diff --git a/test/pecoff/Inputs/armnt-blx23t.obj.yaml b/test/pecoff/Inputs/armnt-blx23t.obj.yaml new file mode 100644 index 0000000000000..03f82609f4e6d --- /dev/null +++ b/test/pecoff/Inputs/armnt-blx23t.obj.yaml @@ -0,0 +1,39 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_ARMNT + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_PURGEABLE, IMAGE_SCN_MEM_16BIT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 704700BF2DE90048EB46202000F000F80130BDE80088 + Relocations: + - VirtualAddress: 12 + SymbolName: identity + Type: 21 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 22 + NumberOfRelocations: 1 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: identity + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: function + Value: 4 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/armnt-blx23t.s b/test/pecoff/Inputs/armnt-blx23t.s new file mode 100644 index 0000000000000..89aa4194faaef --- /dev/null +++ b/test/pecoff/Inputs/armnt-blx23t.s @@ -0,0 +1,33 @@ + +# __declspec(noinline) int identity(int i) { return i; } +# int function() { return identity(32) + 1; } + + .syntax unified + .thumb + .text + + .def identity + .scl 2 + .type 32 + .endef + .global identity + .align 2 + .thumb_func +identity: + bx lr + + .def function + .scl 2 + .type 32 + .endef + .global function + .align 2 + .thumb_func +function: + push.w {r11, lr} + mov r11, sp + movs r0, 32 + bl identity + adds r0, 1 + pop.w {r11, pc} + diff --git a/test/pecoff/Inputs/armnt-branch24t.obj.yaml b/test/pecoff/Inputs/armnt-branch24t.obj.yaml new file mode 100644 index 0000000000000..02815a4957dd3 --- /dev/null +++ b/test/pecoff/Inputs/armnt-branch24t.obj.yaml @@ -0,0 +1,39 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_ARMNT + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_PURGEABLE, IMAGE_SCN_MEM_16BIT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 704700BF202000F000B8 + Relocations: + - VirtualAddress: 6 + SymbolName: identity + Type: 20 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 10 + NumberOfRelocations: 1 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: identity + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: function + Value: 4 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/armnt-branch24t.s b/test/pecoff/Inputs/armnt-branch24t.s new file mode 100644 index 0000000000000..8d85cbe0cab23 --- /dev/null +++ b/test/pecoff/Inputs/armnt-branch24t.s @@ -0,0 +1,26 @@ + +# int ___declspec(noinline) identity(int i) { return i; } +# int function(void) { return identity(32); } + + .syntax unified + .thumb + .text + + .def identity + .scl 2 + .type 32 + .endef + .global identity + .align 2 + .thumb_func +identity: + bx lr + + .def function + .scl 2 + .type 32 + .endef +function: + movs r0, 32 + b identity + diff --git a/test/pecoff/Inputs/armnt-exports.def b/test/pecoff/Inputs/armnt-exports.def new file mode 100644 index 0000000000000..afb8258f95bc0 --- /dev/null +++ b/test/pecoff/Inputs/armnt-exports.def @@ -0,0 +1,4 @@ +LIBRARY "armnt-exports" +EXPORTS + function + diff --git a/test/pecoff/Inputs/armnt-exports.obj.yaml b/test/pecoff/Inputs/armnt-exports.obj.yaml new file mode 100644 index 0000000000000..2a971db2fdaa3 --- /dev/null +++ b/test/pecoff/Inputs/armnt-exports.obj.yaml @@ -0,0 +1,35 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_ARMNT + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_PURGEABLE, IMAGE_SCN_MEM_16BIT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 704700BF7047 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 6 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: function + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _DllMainCRTStartup + Value: 4 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/armnt-import.obj.yaml b/test/pecoff/Inputs/armnt-import.obj.yaml new file mode 100644 index 0000000000000..08876eeb0d4c6 --- /dev/null +++ b/test/pecoff/Inputs/armnt-import.obj.yaml @@ -0,0 +1,39 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_ARMNT + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_PURGEABLE, IMAGE_SCN_MEM_16BIT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 40F20000C0F2000000680047 + Relocations: + - VirtualAddress: 0 + SymbolName: __imp_function + Type: 17 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 12 + NumberOfRelocations: 1 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: mainCRTStartup + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: __imp_function + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/armnt-import.s b/test/pecoff/Inputs/armnt-import.s new file mode 100644 index 0000000000000..2790d0edc8d9a --- /dev/null +++ b/test/pecoff/Inputs/armnt-import.s @@ -0,0 +1,21 @@ + +# void __declspec(dllimport) function(void); +# int mainCRTStartup(void) { return function(); } + + .syntax unified + .thumb + .text + + .def mainCRTStartup + .scl 2 + .type 32 + .endef + .global mainCRTStartup + .align 2 + .thumb_func +mainCRTStartup: + movw r0, :lower16:__imp_function + movt r0, :upper16:__imp_function + ldr r0, [r0] + bx r0 + diff --git a/test/pecoff/Inputs/armnt-mov32t-exec.obj.yaml b/test/pecoff/Inputs/armnt-mov32t-exec.obj.yaml new file mode 100644 index 0000000000000..a4630bcb9e0e5 --- /dev/null +++ b/test/pecoff/Inputs/armnt-mov32t-exec.obj.yaml @@ -0,0 +1,39 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_ARMNT + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_PURGEABLE, IMAGE_SCN_MEM_16BIT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 704700BF40F20000C0F200007047 + Relocations: + - VirtualAddress: 4 + SymbolName: function + Type: 17 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 14 + NumberOfRelocations: 1 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: function + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: get_function + Value: 4 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/armnt-mov32t-exec.s b/test/pecoff/Inputs/armnt-mov32t-exec.s new file mode 100644 index 0000000000000..10a20f468c345 --- /dev/null +++ b/test/pecoff/Inputs/armnt-mov32t-exec.s @@ -0,0 +1,30 @@ + +# void function(void) { } +# void *get_function() { return &function; } + + .syntax unified + .thumb + .text + + .def function + .scl 2 + .type 32 + .endef + .global function + .align 2 + .thumb_func +function: + bx lr + + .def get_function + .scl 2 + .type 32 + .endef + .global get_function + .align 2 + .thumb_func +get_function: + movw r0, :lower16:function + movt r0, :upper16:function + bx lr + diff --git a/test/pecoff/Inputs/armnt-mov32t.obj.yaml b/test/pecoff/Inputs/armnt-mov32t.obj.yaml new file mode 100644 index 0000000000000..21e890ace28f0 --- /dev/null +++ b/test/pecoff/Inputs/armnt-mov32t.obj.yaml @@ -0,0 +1,55 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_ARMNT + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_PURGEABLE, IMAGE_SCN_MEM_16BIT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 40F20000C0F200007047 + Relocations: + - VirtualAddress: 0 + SymbolName: buffer + Type: 17 + - Name: .rdata + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 1 + SectionData: '62756666657200' +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 10 + NumberOfRelocations: 1 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: .rdata + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 7 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 2 + - Name: get_buffer + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: buffer + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC +... diff --git a/test/pecoff/Inputs/armnt-mov32t.s b/test/pecoff/Inputs/armnt-mov32t.s new file mode 100644 index 0000000000000..289f917597d69 --- /dev/null +++ b/test/pecoff/Inputs/armnt-mov32t.s @@ -0,0 +1,24 @@ + +# static const char buffer[] = "buffer"; +# const char *get_buffer() { return buffer; } + + .syntax unified + .thumb + .text + + .def get_buffer + .scl 2 + .type 32 + .endef + .global get_buffer + .align 2 + .thumb_func +get_buffer: + movw r0, :lower16:buffer + movt r0, :upper16:buffer + bx lr + + .section .rdata,"rd" +buffer: + .asciz "buffer" + diff --git a/test/pecoff/Inputs/armnt-obj.s b/test/pecoff/Inputs/armnt-obj.s new file mode 100644 index 0000000000000..20eeab0e1dda0 --- /dev/null +++ b/test/pecoff/Inputs/armnt-obj.s @@ -0,0 +1,12 @@ + + .syntax unified + .thumb + .text + + .def main + .scl 2 + .type 32 + .endef +main: + bx lr + diff --git a/test/pecoff/Inputs/armnt-obj.yaml b/test/pecoff/Inputs/armnt-obj.yaml new file mode 100644 index 0000000000000..7c53c6f00693f --- /dev/null +++ b/test/pecoff/Inputs/armnt-obj.yaml @@ -0,0 +1,29 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_ARMNT + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_PURGEABLE, IMAGE_SCN_MEM_16BIT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: '7047' +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 2 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/associative1.obj.yaml b/test/pecoff/Inputs/associative1.obj.yaml new file mode 100644 index 0000000000000..51a7be0b34d70 --- /dev/null +++ b/test/pecoff/Inputs/associative1.obj.yaml @@ -0,0 +1,53 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [] +sections: + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: 00000000 + - Name: '.CRT$XCU' + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 77777777 +symbols: + - Name: .data + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 4 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + Selection: IMAGE_COMDAT_SELECT_ANY + - Name: _var + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: '.CRT$XCU' + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 4 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE + - Name: _init + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/associative3.obj.yaml b/test/pecoff/Inputs/associative3.obj.yaml new file mode 100644 index 0000000000000..ffea761a357c1 --- /dev/null +++ b/test/pecoff/Inputs/associative3.obj.yaml @@ -0,0 +1,33 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 0000000000000000 + Relocations: + - VirtualAddress: 4 + SymbolName: _var + Type: IMAGE_REL_I386_DIR32 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: _main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _var + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/basereloc.obj.yaml b/test/pecoff/Inputs/basereloc.obj.yaml new file mode 100644 index 0000000000000..5e5ca16a0ac72 --- /dev/null +++ b/test/pecoff/Inputs/basereloc.obj.yaml @@ -0,0 +1,164 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4096 + SectionData: B800000000506800000000680000000050E80000000050E80000000050E800000000 + Relocations: + - VirtualAddress: 0 + SymbolName: abs_symbol + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 7 + SymbolName: caption + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 12 + SymbolName: message + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 18 + SymbolName: _MessageBoxA@16 + Type: IMAGE_REL_I386_REL32 + - VirtualAddress: 24 + SymbolName: _ExitProcess@4 + Type: IMAGE_REL_I386_REL32 + - VirtualAddress: 30 + SymbolName: ___ImageBase + Type: IMAGE_REL_I386_DIR32 + - Name: .text2 + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4096 + SectionData: B800000000506800000000680000000050E80000000050E80000000050E800000000 + Relocations: + - VirtualAddress: 0 + SymbolName: abs_symbol + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 7 + SymbolName: caption + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 12 + SymbolName: message + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 18 + SymbolName: _MessageBoxA@16 + Type: IMAGE_REL_I386_REL32 + - VirtualAddress: 24 + SymbolName: _ExitProcess@4 + Type: IMAGE_REL_I386_REL32 + - VirtualAddress: 30 + SymbolName: ___ImageBase + Type: IMAGE_REL_I386_DIR32 + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: 48656C6C6F0048656C6C6F20576F726C6400 + - Name: .drectve + Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ] + Alignment: 2147483648 + SectionData: 2F454E5452593A6D61696E20 +symbols: + - Name: "@comp.id" + Value: 10394907 + SectionNumber: 65535 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 28 + NumberOfRelocations: 6 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: .text2 + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 28 + NumberOfRelocations: 6 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: .data + Value: 0 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 18 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: _MessageBoxA@16 + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _ExitProcess@4 + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: message + Value: 6 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: _main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: caption + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .drectve + Value: 0 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 12 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: .file + Value: 0 + SectionNumber: 65534 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + File: "hello.c" + - Name: abs_symbol + Value: 0xDEADBEEF + SectionNumber: -1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: ___ImageBase + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/bss.asm b/test/pecoff/Inputs/bss.asm new file mode 100644 index 0000000000000..802edee8591d8 --- /dev/null +++ b/test/pecoff/Inputs/bss.asm @@ -0,0 +1,20 @@ +.586 +.model flat, c + +extern ExitProcess@4 : PROC + +_BSS SEGMENT + _x DD 064H DUP (?) + _y DD 064H DUP (?) +_BSS ENDS + +.code +start: + mov eax, 42 + mov _x, eax + mov eax, _x + push eax + call ExitProcess@4 +end start + +end diff --git a/test/pecoff/Inputs/bss.obj b/test/pecoff/Inputs/bss.obj Binary files differnew file mode 100644 index 0000000000000..3c00dfeb830bb --- /dev/null +++ b/test/pecoff/Inputs/bss.obj diff --git a/test/pecoff/Inputs/comdat.obj.yaml b/test/pecoff/Inputs/comdat.obj.yaml new file mode 100644 index 0000000000000..5537499873a36 --- /dev/null +++ b/test/pecoff/Inputs/comdat.obj.yaml @@ -0,0 +1,53 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: 558BEC33C05DC3 + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: 558BEC33C05DC3 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 7 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 2532800969 + Number: 0 + Selection: IMAGE_COMDAT_SELECT_ANY + - Name: .text + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 7 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 2532800969 + Number: 0 + Selection: IMAGE_COMDAT_SELECT_ANY + - Name: "?inlinefn1@@YAHXZ" + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: "?inlinefn2@@YAHXZ" + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/common-symbol.obj.yaml b/test/pecoff/Inputs/common-symbol.obj.yaml new file mode 100644 index 0000000000000..05ddd022f2868 --- /dev/null +++ b/test/pecoff/Inputs/common-symbol.obj.yaml @@ -0,0 +1,85 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: b800000000b800000000b800000000b800000000b800000000 + Relocations: + - VirtualAddress: 1 + SymbolName: _bssdata4 + Type: IMAGE_REL_AMD64_ADDR32 + - VirtualAddress: 6 + SymbolName: _bsspad1 + Type: IMAGE_REL_AMD64_ADDR32 + - VirtualAddress: 11 + SymbolName: _bssdata64 + Type: IMAGE_REL_AMD64_ADDR32 + - VirtualAddress: 16 + SymbolName: _bsspad2 + Type: IMAGE_REL_AMD64_ADDR32 + - VirtualAddress: 21 + SymbolName: _bssdata16 + Type: IMAGE_REL_AMD64_ADDR32 + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: 03000000 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 0 + NumberOfRelocations: 5 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: .data + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 4 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: _bssdata4 + Value: 4 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _bsspad1 + Value: 1 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _bssdata64 + Value: 64 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _bsspad2 + Value: 1 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _bssdata16 + Value: 16 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/drectve.obj.yaml b/test/pecoff/Inputs/drectve.obj.yaml new file mode 100644 index 0000000000000..22ec63f96e3e9 --- /dev/null +++ b/test/pecoff/Inputs/drectve.obj.yaml @@ -0,0 +1,79 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: 558BEC56FF15000000008B0D000000008B3103F0FF150000000003C65E5DC3 + Relocations: + - VirtualAddress: 6 + SymbolName: __imp__fn + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 12 + SymbolName: __imp__var + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 22 + SymbolName: __imp___name_with_underscore + Type: IMAGE_REL_I386_DIR32 + - Name: .drectve + Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ] + Alignment: 2147483648 + SectionData: 2f64656661756c746c69623a766172732e6c6962202f73756273797374656d3a636f6e736f6c652c34322e313935202d3f666f6f00 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 31 + NumberOfRelocations: 3 + NumberOfLinenumbers: 0 + CheckSum: 3595596940 + Number: 0 + - Name: __imp__fn + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: __imp___name_with_underscore + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: __imp__var + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _fn + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: .drectve + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 13 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 +... diff --git a/test/pecoff/Inputs/drectve2.obj.yaml b/test/pecoff/Inputs/drectve2.obj.yaml new file mode 100644 index 0000000000000..836cbc3e743d3 --- /dev/null +++ b/test/pecoff/Inputs/drectve2.obj.yaml @@ -0,0 +1,45 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: 558BEC56FF15000000008B0D000000008B3103F0FF150000000003C65E5DC3 + - Name: .drectve + Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ] + Alignment: 2147483648 + SectionData: 2f696e636c7564653a666f6f00 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 31 + NumberOfRelocations: 3 + NumberOfLinenumbers: 0 + CheckSum: 3595596940 + Number: 0 + - Name: _main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: .drectve + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 13 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 +... diff --git a/test/pecoff/Inputs/drectve3.lib b/test/pecoff/Inputs/drectve3.lib Binary files differnew file mode 100644 index 0000000000000..c295d1ff6d045 --- /dev/null +++ b/test/pecoff/Inputs/drectve3.lib diff --git a/test/pecoff/Inputs/entry.obj.yaml b/test/pecoff/Inputs/entry.obj.yaml new file mode 100644 index 0000000000000..35eae143c4302 --- /dev/null +++ b/test/pecoff/Inputs/entry.obj.yaml @@ -0,0 +1,40 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] + +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: A100000000030500000000C3 + +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + + - Name: _foo + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + + - Name: _bar + Value: 4 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + + - Name: "?baz@@YAXXZ" + Value: 4 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/executable.obj.yaml b/test/pecoff/Inputs/executable.obj.yaml new file mode 100644 index 0000000000000..73ba0fe719146 --- /dev/null +++ b/test/pecoff/Inputs/executable.obj.yaml @@ -0,0 +1,29 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_ARMNT + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_PURGEABLE, IMAGE_SCN_MEM_16BIT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: '7047' +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 2 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: mainCRTStartup + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/executable.s b/test/pecoff/Inputs/executable.s new file mode 100644 index 0000000000000..1c58f7331b266 --- /dev/null +++ b/test/pecoff/Inputs/executable.s @@ -0,0 +1,17 @@ + +# void mainCRTStartup(){} + + .syntax unified + .thumb + .text + + .def mainCRTStartup + .scl 2 + .type 32 + .endef + .global mainCRTStartup + .align 2 + .thumb_func +mainCRTStartup: + bx lr + diff --git a/test/pecoff/Inputs/export.obj.yaml b/test/pecoff/Inputs/export.obj.yaml new file mode 100644 index 0000000000000..fa92cd09bee4c --- /dev/null +++ b/test/pecoff/Inputs/export.obj.yaml @@ -0,0 +1,69 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: B800000000506800000000680000000050E80000000050E800000000 + - Name: .drectve + Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ] + Alignment: 2147483648 + SectionData: 2f6578706f72743a5f6578706f7274666e334032353600 # /export:_exportfn3@256 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 28 + NumberOfRelocations: 4 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: _init + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _exportfn1 + Value: 8 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _exportfn2 + Value: 16 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _exportfn3@256 + Value: 16 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _exportfn6 + Value: 16 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _exportfn7@8 + Value: 16 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: "?exportfn8@@YAXXZ" + Value: 16 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/exports.def b/test/pecoff/Inputs/exports.def new file mode 100644 index 0000000000000..7b444f9719d1c --- /dev/null +++ b/test/pecoff/Inputs/exports.def @@ -0,0 +1,6 @@ +; This is a comment line + +EXPORTS + exportfn1 @5 ; foo + exportfn2 + exportfn5=exportfn6 PRIVATE diff --git a/test/pecoff/Inputs/exports2.def b/test/pecoff/Inputs/exports2.def new file mode 100644 index 0000000000000..1c95f42ceb60f --- /dev/null +++ b/test/pecoff/Inputs/exports2.def @@ -0,0 +1,6 @@ +; This is a comment line + +EXPORTS + exportfn1 @5 ; foo + exportfn7 + exportfn5=exportfn6 PRIVATE diff --git a/test/pecoff/Inputs/grouped-sections.asm b/test/pecoff/Inputs/grouped-sections.asm new file mode 100644 index 0000000000000..af69363efefbd --- /dev/null +++ b/test/pecoff/Inputs/grouped-sections.asm @@ -0,0 +1,18 @@ +.386 +.model flat, c + +_data$2 SEGMENT BYTE alias(".data$2") + db "orld", 0 +_data$2 ends + +_data$1 SEGMENT BYTE alias(".data$1") + db "o, w" +_data$1 ends + +.data + db "Hell" + +.code +main: + nop +end main diff --git a/test/pecoff/Inputs/grouped-sections.obj.yaml b/test/pecoff/Inputs/grouped-sections.obj.yaml new file mode 100644 index 0000000000000..2180312acf92e --- /dev/null +++ b/test/pecoff/Inputs/grouped-sections.obj.yaml @@ -0,0 +1,83 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 90 + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: 48656C6C + - Name: ".data$2" + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 1 + SectionData: 6F726C6400 + - Name: ".data$1" + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 1 + SectionData: 6F2C2077 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: .data + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 4 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: ".data$2" + Value: 0 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 5 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: ".data$1" + Value: 0 + SectionNumber: 4 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 4 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: foo + Value: 2 + SectionNumber: 4 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: _main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/hello.asm b/test/pecoff/Inputs/hello.asm new file mode 100644 index 0000000000000..e360bbf657066 --- /dev/null +++ b/test/pecoff/Inputs/hello.asm @@ -0,0 +1,24 @@ +;;; ml hello.asm /link /subsystem:windows /defaultlib:kernel32.lib \ +;;; /defaultlib:user32.lib /out:hello.exe /entry:main + +.386 +.model flat, c + +extern MessageBoxA@16 : PROC +extern ExitProcess@4 : PROC + +.data + caption db "Hello", 0 + message db "Hello World", 0 + +.code +main: + mov eax, 0 + push eax + push offset caption + push offset message + push eax + call MessageBoxA@16 + push eax + call ExitProcess@4 +end main diff --git a/test/pecoff/Inputs/hello.obj.yaml b/test/pecoff/Inputs/hello.obj.yaml new file mode 100644 index 0000000000000..6268a74057bd5 --- /dev/null +++ b/test/pecoff/Inputs/hello.obj.yaml @@ -0,0 +1,111 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: B800000000506800000000680000000050E80000000050E800000000 + Relocations: + - VirtualAddress: 7 + SymbolName: caption + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 12 + SymbolName: message + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 18 + SymbolName: _MessageBoxA@16 + Type: IMAGE_REL_I386_REL32 + - VirtualAddress: 24 + SymbolName: _ExitProcess@4 + Type: IMAGE_REL_I386_REL32 + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: 48656C6C6F0048656C6C6F20576F726C6400 + - Name: .drectve + Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ] + Alignment: 2147483648 + SectionData: 2F454E5452593A6D61696E20 +symbols: + - Name: "@comp.id" + Value: 10394907 + SectionNumber: 65535 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 28 + NumberOfRelocations: 4 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: .data + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 18 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: _MessageBoxA@16 + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _ExitProcess@4 + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: message + Value: 6 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: _main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: caption + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .drectve + Value: 0 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 12 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: .file + Value: 0 + SectionNumber: 65534 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + File: "hello.c" +... diff --git a/test/pecoff/Inputs/hello64.asm b/test/pecoff/Inputs/hello64.asm new file mode 100644 index 0000000000000..bc1a41b500d0a --- /dev/null +++ b/test/pecoff/Inputs/hello64.asm @@ -0,0 +1,22 @@ +;; ml hello64.asm /link /subsystem:windows /defaultlib:kernel32 \ +;; /defaultlib:user32 /out:hello64.exe /entry:main + +extern ExitProcess : PROC +extern MessageBoxA : PROC + +.data + caption db 'Hello', 0 + message db 'Hello World', 0 + +.code +main PROC + sub rsp,28h + mov rcx, 0 + lea rdx, message + lea r8, caption + mov r9d, 0 + call MessageBoxA + mov ecx, 0 + call ExitProcess +main ENDP +END diff --git a/test/pecoff/Inputs/hello64.obj.yaml b/test/pecoff/Inputs/hello64.obj.yaml new file mode 100644 index 0000000000000..36b699623101b --- /dev/null +++ b/test/pecoff/Inputs/hello64.obj.yaml @@ -0,0 +1,110 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: '.text$mn' + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: 4883EC2848C7C100000000488D15000000004C8D050000000041B900000000E800000000B900000000E800000000 + Relocations: + - VirtualAddress: 14 + SymbolName: message + Type: IMAGE_REL_AMD64_REL32 + - VirtualAddress: 21 + SymbolName: caption + Type: IMAGE_REL_AMD64_REL32 + - VirtualAddress: 32 + SymbolName: MessageBoxA + Type: IMAGE_REL_AMD64_REL32 + - VirtualAddress: 42 + SymbolName: ExitProcess + Type: IMAGE_REL_AMD64_REL32 + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 16 + SectionData: 48656C6C6F0048656C6C6F20576F726C6400 + - Name: '.debug$S' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ] + Alignment: 1 + SectionData: 04000000F1000000830000004800011100000000433A5C63796777696E5C686F6D655C727569755C6C6C766D5C746F6F6C735C6C6C645C746573745C7065636F66665C496E707574735C68656C6C6F36342E6F626A0037003C1103020000D00000000000000000000C0000000D5201004D6963726F736F667420285229204D6163726F20417373656D626C6572000000 +symbols: + - Name: '@comp.id' + Value: 14635533 + SectionNumber: 65535 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: '@feat.00' + Value: 16 + SectionNumber: 65535 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: '.text$mn' + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 46 + NumberOfRelocations: 4 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: .data + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 18 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: '.debug$S' + Value: 0 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 144 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: ExitProcess + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: MessageBoxA + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: message + Value: 6 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: caption + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/hello64lib.asm b/test/pecoff/Inputs/hello64lib.asm new file mode 100644 index 0000000000000..89f95cd49af15 --- /dev/null +++ b/test/pecoff/Inputs/hello64lib.asm @@ -0,0 +1,14 @@ +.code +ExitProcess PROC + RET +ExitProcess ENDP + +MessageBoxA PROC + RET +MessageBoxA ENDP + +_DllMainCRTStartup PROC + RET +_DllMainCRTStartup ENDP + +END diff --git a/test/pecoff/Inputs/hello64lib.lib b/test/pecoff/Inputs/hello64lib.lib Binary files differnew file mode 100644 index 0000000000000..3109c32593ca8 --- /dev/null +++ b/test/pecoff/Inputs/hello64lib.lib diff --git a/test/pecoff/Inputs/imagebase.obj.yaml b/test/pecoff/Inputs/imagebase.obj.yaml new file mode 100644 index 0000000000000..e31e744fa058c --- /dev/null +++ b/test/pecoff/Inputs/imagebase.obj.yaml @@ -0,0 +1,55 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: A100000000C3 + Relocations: + - VirtualAddress: 1 + SymbolName: ___ImageBase + Type: IMAGE_REL_I386_DIR32 + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 16 + SectionData: "" +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 6 + NumberOfRelocations: 1 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: .data + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 0 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: ___ImageBase + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: __start + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/library.lib b/test/pecoff/Inputs/library.lib Binary files differnew file mode 100755 index 0000000000000..2f4207d7983da --- /dev/null +++ b/test/pecoff/Inputs/library.lib diff --git a/test/pecoff/Inputs/machine-type-unknown.obj.yaml b/test/pecoff/Inputs/machine-type-unknown.obj.yaml new file mode 100644 index 0000000000000..f0da1ea7ac341 --- /dev/null +++ b/test/pecoff/Inputs/machine-type-unknown.obj.yaml @@ -0,0 +1,38 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_UNKNOWN + Characteristics: [] +sections: + - Name: .drectve + Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ] + Alignment: 2147483648 + SectionData: '' +symbols: + - Name: '@comp.id' + Value: 1 + SectionNumber: 65535 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: '@feat.00' + Value: 1 + SectionNumber: 65535 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: __imp___close + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: __imp__close + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_WEAK_EXTERNAL + WeakExternal: + TagIndex: 2 + Characteristics: IMAGE_WEAK_EXTERN_SEARCH_ALIAS +... diff --git a/test/pecoff/Inputs/main.obj.yaml b/test/pecoff/Inputs/main.obj.yaml new file mode 100644 index 0000000000000..73a788049063e --- /dev/null +++ b/test/pecoff/Inputs/main.obj.yaml @@ -0,0 +1,70 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: A100000000030500000000C3 + Relocations: + - VirtualAddress: 1 + SymbolName: _val1 + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 7 + SymbolName: _val2 + Type: IMAGE_REL_I386_DIR32 + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: "" +symbols: + - Name: "@comp.id" + Value: 10394907 + SectionNumber: 65535 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 12 + NumberOfRelocations: 2 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: .data + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 0 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: _val1 + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _val2 + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/merge-largest1.obj.yaml b/test/pecoff/Inputs/merge-largest1.obj.yaml new file mode 100644 index 0000000000000..e372f90306034 --- /dev/null +++ b/test/pecoff/Inputs/merge-largest1.obj.yaml @@ -0,0 +1,30 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: 00112233 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 7 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 2532800969 + Number: 0 + Selection: IMAGE_COMDAT_SELECT_LARGEST + - Name: "_foo" + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/merge-largest2.obj.yaml b/test/pecoff/Inputs/merge-largest2.obj.yaml new file mode 100644 index 0000000000000..f232cd3fd8700 --- /dev/null +++ b/test/pecoff/Inputs/merge-largest2.obj.yaml @@ -0,0 +1,30 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: 0011223344556677 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 7 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 2532800969 + Number: 0 + Selection: IMAGE_COMDAT_SELECT_LARGEST + - Name: "_foo" + Value: 6 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/merge-same-size1.obj.yaml b/test/pecoff/Inputs/merge-same-size1.obj.yaml new file mode 100644 index 0000000000000..02516f37220e9 --- /dev/null +++ b/test/pecoff/Inputs/merge-same-size1.obj.yaml @@ -0,0 +1,30 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: FFFFFFFFFFFFFF +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 7 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 2532800969 + Number: 0 + Selection: IMAGE_COMDAT_SELECT_SAME_SIZE + - Name: "_foo" + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/merge-same-size2.obj.yaml b/test/pecoff/Inputs/merge-same-size2.obj.yaml new file mode 100644 index 0000000000000..6b5b593bbab14 --- /dev/null +++ b/test/pecoff/Inputs/merge-same-size2.obj.yaml @@ -0,0 +1,30 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: AAAAAAAAAAAAAA +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 7 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 2532800969 + Number: 0 + Selection: IMAGE_COMDAT_SELECT_SAME_SIZE + - Name: "_foo" + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/merge-same-size3.obj.yaml b/test/pecoff/Inputs/merge-same-size3.obj.yaml new file mode 100644 index 0000000000000..47ad417f2f0ce --- /dev/null +++ b/test/pecoff/Inputs/merge-same-size3.obj.yaml @@ -0,0 +1,30 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: FFFF +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 2 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 2532800969 + Number: 0 + Selection: IMAGE_COMDAT_SELECT_SAME_SIZE + - Name: "_foo" + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/nonstandard-sections.obj.yaml b/test/pecoff/Inputs/nonstandard-sections.obj.yaml new file mode 100644 index 0000000000000..b80d8862ec619 --- /dev/null +++ b/test/pecoff/Inputs/nonstandard-sections.obj.yaml @@ -0,0 +1,53 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 01234678 + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: 01234678 + - Name: .foo + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE, IMAGE_SCN_MEM_EXECUTE ] + Alignment: 4 + SectionData: 01234678 + - Name: .bar + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 01234678 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .data + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .foo + Value: 0 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .bar + Value: 0 + SectionNumber: 4 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: _main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/nop.asm b/test/pecoff/Inputs/nop.asm new file mode 100644 index 0000000000000..8e53070e7f5cf --- /dev/null +++ b/test/pecoff/Inputs/nop.asm @@ -0,0 +1,9 @@ +.386 +.model flat, stdcall +option casemap :none + +.code +start: + mov eax, 42 + ret +end start diff --git a/test/pecoff/Inputs/nop.obj.yaml b/test/pecoff/Inputs/nop.obj.yaml new file mode 100644 index 0000000000000..ccc097e7c9263 --- /dev/null +++ b/test/pecoff/Inputs/nop.obj.yaml @@ -0,0 +1,51 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: B82A000000C3 + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: "" +symbols: + - Name: "@comp.id" + Value: 10394907 + SectionNumber: 65535 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 6 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: .data + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 0 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: _start + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/nop64.obj.yaml b/test/pecoff/Inputs/nop64.obj.yaml new file mode 100644 index 0000000000000..66edc376a90ef --- /dev/null +++ b/test/pecoff/Inputs/nop64.obj.yaml @@ -0,0 +1,67 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3C3C3C3 + Relocations: + - VirtualAddress: 0 + SymbolName: __imp__fn + Type: IMAGE_REL_AMD64_REL32 + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 16 + SectionData: '' +symbols: + - Name: '@comp.id' + Value: 13485607 + SectionNumber: 65535 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: '@feat.00' + Value: 16 + SectionNumber: 65535 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 1 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: .data + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 0 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: __imp__fn + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: start + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/reloc.obj.yaml b/test/pecoff/Inputs/reloc.obj.yaml new file mode 100644 index 0000000000000..4c83258c623c7 --- /dev/null +++ b/test/pecoff/Inputs/reloc.obj.yaml @@ -0,0 +1,82 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: 680000000068000000006800000000680000000068000000006800000000 + Relocations: + - VirtualAddress: 1 + SymbolName: _message + Type: IMAGE_REL_I386_SECTION + - VirtualAddress: 6 + SymbolName: _message + Type: IMAGE_REL_I386_SECREL + - VirtualAddress: 11 + SymbolName: .data + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 16 + SymbolName: .data + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 21 + SymbolName: __imp__MessageBoxA@16 + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 26 + SymbolName: _abs_value + Type: IMAGE_REL_I386_DIR32 + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: 576F726C64210048656C6C6F2C00 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 60 + NumberOfRelocations: 3 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: .data + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 14 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 2 + - Name: _main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _message + Value: 5 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: __imp__MessageBoxA@16 + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _abs_value + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/reloc64.obj.yaml b/test/pecoff/Inputs/reloc64.obj.yaml new file mode 100644 index 0000000000000..3230ca51c6ccb --- /dev/null +++ b/test/pecoff/Inputs/reloc64.obj.yaml @@ -0,0 +1,63 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: 48B800000000000000ffE8000000ffE8000000ffE8000000ffE8000000ffE8000000ffE8000000ffE8000000ffE8000000ffC3 + Relocations: + - VirtualAddress: 2 + SymbolName: end + Type: IMAGE_REL_AMD64_ADDR64 + - VirtualAddress: 11 + SymbolName: end + Type: IMAGE_REL_AMD64_REL32 + - VirtualAddress: 16 + SymbolName: end + Type: IMAGE_REL_AMD64_REL32_1 + - VirtualAddress: 21 + SymbolName: end + Type: IMAGE_REL_AMD64_REL32_2 + - VirtualAddress: 26 + SymbolName: end + Type: IMAGE_REL_AMD64_REL32_3 + - VirtualAddress: 31 + SymbolName: end + Type: IMAGE_REL_AMD64_REL32_4 + - VirtualAddress: 36 + SymbolName: end + Type: IMAGE_REL_AMD64_REL32_5 + - VirtualAddress: 41 + SymbolName: end + Type: IMAGE_REL_AMD64_SECTION + - VirtualAddress: 46 + SymbolName: end + Type: IMAGE_REL_AMD64_SECREL +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 41 + NumberOfRelocations: 7 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: entry + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: end + Value: 40 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/resource.rc b/test/pecoff/Inputs/resource.rc new file mode 100644 index 0000000000000..df933e83b77af --- /dev/null +++ b/test/pecoff/Inputs/resource.rc @@ -0,0 +1,4 @@ +STRINGTABLE +{ + 1, "Hello" +} diff --git a/test/pecoff/Inputs/resource.res b/test/pecoff/Inputs/resource.res Binary files differnew file mode 100755 index 0000000000000..f1c799fbbb08f --- /dev/null +++ b/test/pecoff/Inputs/resource.res diff --git a/test/pecoff/Inputs/responsefile.txt b/test/pecoff/Inputs/responsefile.txt new file mode 100644 index 0000000000000..08286119d4ed3 --- /dev/null +++ b/test/pecoff/Inputs/responsefile.txt @@ -0,0 +1 @@ +-foo -bar\baz diff --git a/test/pecoff/Inputs/secrel1.obj.yaml b/test/pecoff/Inputs/secrel1.obj.yaml new file mode 100644 index 0000000000000..1c49261bbc435 --- /dev/null +++ b/test/pecoff/Inputs/secrel1.obj.yaml @@ -0,0 +1,69 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: C3 + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: 00000000000000000000000000000000 + Relocations: + - VirtualAddress: 0 + SymbolName: .data + Type: IMAGE_REL_I386_SECREL + - Name: .data2 + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: 00000000000000000000000000000000 + Relocations: + - VirtualAddress: 0 + SymbolName: .data2 + Type: IMAGE_REL_I386_SECREL +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 60 + NumberOfRelocations: 1 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: .data + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 4 + NumberOfRelocations: 1 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 2 + - Name: .data2 + Value: 0 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 4 + NumberOfRelocations: 1 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 3 + - Name: _main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/secrel2.obj.yaml b/test/pecoff/Inputs/secrel2.obj.yaml new file mode 100644 index 0000000000000..2885fd07bb3a0 --- /dev/null +++ b/test/pecoff/Inputs/secrel2.obj.yaml @@ -0,0 +1,47 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: 00000000000000000000000000000000 + Relocations: + - VirtualAddress: 0 + SymbolName: .data + Type: IMAGE_REL_I386_SECREL + - Name: .data2 + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: 00000000000000000000000000000000 + Relocations: + - VirtualAddress: 0 + SymbolName: .data2 + Type: IMAGE_REL_I386_SECREL +symbols: + - Name: .data + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 4 + NumberOfRelocations: 1 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: .data2 + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 4 + NumberOfRelocations: 1 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 2 +... diff --git a/test/pecoff/Inputs/seh.c b/test/pecoff/Inputs/seh.c new file mode 100644 index 0000000000000..b1c139a58f229 --- /dev/null +++ b/test/pecoff/Inputs/seh.c @@ -0,0 +1,13 @@ +__declspec(noinline) void triggerSEH() { + volatile int *p = 0; + *p = 1; +} + +int main() { + __try { + triggerSEH(); + } __except(1) { + return 42; + } + return 0; +} diff --git a/test/pecoff/Inputs/seh.obj.yaml b/test/pecoff/Inputs/seh.obj.yaml new file mode 100644 index 0000000000000..6767671cdafec --- /dev/null +++ b/test/pecoff/Inputs/seh.obj.yaml @@ -0,0 +1,387 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .drectve + Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ] + Alignment: 1 + SectionData: 2020202F44454641554C544C49423A22757569642E6C696222202F44454641554C544C49423A22757569642E6C696222202F4641494C49464D49534D415443483A225F4D53435F5645523D3138303022202F4641494C49464D49534D415443483A225F4954455241544F525F44454255475F4C4556454C3D3022202F4641494C49464D49534D415443483A2252756E74696D654C6962726172793D4D445F44796E616D696352656C6561736522202F44454641554C544C49423A226D73766370727422202F44454641554C544C49423A224D535643525422202F44454641554C544C49423A224F4C444E414D45532220 + - Name: '.debug$S' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ] + Alignment: 1 + SectionData: 04000000F1000000600000002200011100000000433A5C63796777696E5C686F6D655C727569755C7365682E6F626A003A003C11012200000700120000000D520100120000000D5201004D6963726F736F667420285229204F7074696D697A696E6720436F6D70696C657200 + - Name: .rdata + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ ] + Alignment: 1 + SectionData: 00 + - Name: .rdata + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ ] + Alignment: 1 + SectionData: 01 + - Name: .rdata + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 54726967676572696E672053454820657863657074696F6E0D0A0000457865637574696E6720534548205F5F65786365707420626C6F636B20696E20666F6F0D0A000000457865637574696E6720534548205F5F65786365707420626C6F636B0D0A00 + - Name: '.text$mn' + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: 558BEC516800000000FF150000000083C404C745FC000000008B45FCC700140000008BE55DC3CCCCCCCCCCCCCCCCCCCC558BEC51E8000000008D4DFFE8000000008BE55DC3CCCCCCCCCCCCCCCCCCCCCC558BEC6AFE6800000000680000000064A1000000005083EC08535657A1000000003145F833C5508D45F064A3000000008965E8C745FC00000000E800000000C745FCFEFFFFFFEB1EB801000000C38B65E86800000000FF150000000083C404C745FCFEFFFFFF8B4DF064890D00000000595F5E5B8BE55DC3CCCCCCCCCCCCCCCC558BEC6AFE6800000000680000000064A1000000005083EC08535657A1000000003145F833C5508D45F064A3000000008965E8C745FC00000000E800000000E800000000C745FCFEFFFFFFEB1EB801000000C38B65E86800000000FF150000000083C404C745FCFEFFFFFF33C08B4DF064890D00000000595F5E5B8BE55DC3 + Relocations: + - VirtualAddress: 5 + SymbolName: '$SG73531' + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 11 + SymbolName: __imp__printf + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 53 + SymbolName: '?TestCPPEX@@YAXXZ' + Type: IMAGE_REL_I386_REL32 + - VirtualAddress: 61 + SymbolName: '??1TestClass@@QAE@XZ' + Type: IMAGE_REL_I386_REL32 + - VirtualAddress: 86 + SymbolName: '__sehtable$?foo@@YAXXZ' + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 91 + SymbolName: __except_handler4 + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 109 + SymbolName: ___security_cookie + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 139 + SymbolName: '?TestExceptions@@YAXXZ' + Type: IMAGE_REL_I386_REL32 + - VirtualAddress: 162 + SymbolName: '$SG73539' + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 168 + SymbolName: __imp__printf + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 214 + SymbolName: '__sehtable$_main' + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 219 + SymbolName: __except_handler4 + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 237 + SymbolName: ___security_cookie + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 267 + SymbolName: '?foo@@YAXXZ' + Type: IMAGE_REL_I386_REL32 + - VirtualAddress: 272 + SymbolName: '?TestExceptions@@YAXXZ' + Type: IMAGE_REL_I386_REL32 + - VirtualAddress: 295 + SymbolName: '$SG73543' + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 301 + SymbolName: __imp__printf + Type: IMAGE_REL_I386_DIR32 + - Name: '.text$mn' + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: 558BEC51894DFC6800000000FF150000000083C4048BE55DC3 + Relocations: + - VirtualAddress: 8 + SymbolName: '??_C@_0BI@BBHGNMOG@Destroying?5TestClass?$CB?$AN?6?$AA@' + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 14 + SymbolName: __imp__printf + Type: IMAGE_REL_I386_DIR32 + - Name: '.xdata$x' + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 8 + SectionData: FEFFFFFF00000000D8FFFFFF00000000FEFFFFFF000000000000000000000000FEFFFFFF00000000D8FFFFFF00000000FEFFFFFF0000000000000000 + Relocations: + - VirtualAddress: 20 + SymbolName: '$LN5' + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 24 + SymbolName: '$LN6' + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 52 + SymbolName: '$LN5' + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 56 + SymbolName: '$LN6' + Type: IMAGE_REL_I386_DIR32 + - Name: .rdata + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 44657374726F79696E672054657374436C617373210D0A00 + - Name: .sxdata + Characteristics: [ IMAGE_SCN_LNK_INFO ] + Alignment: 4 + SectionData: 1B0000001A000000 +symbols: + - Name: '@comp.id' + Value: 14766605 + SectionNumber: 65535 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: '@feat.00' + Value: 2147484049 + SectionNumber: 65535 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .drectve + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 240 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: '.debug$S' + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 108 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: .rdata + Value: 0 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + Selection: IMAGE_COMDAT_SELECT_ANY + - Name: '?value@?$integral_constant@_N$0A@@std@@2_NB' + Value: 0 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: .rdata + Value: 0 + SectionNumber: 4 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 1996959894 + Number: 0 + Selection: IMAGE_COMDAT_SELECT_ANY + - Name: '?value@?$integral_constant@_N$00@std@@2_NB' + Value: 0 + SectionNumber: 4 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: .rdata + Value: 0 + SectionNumber: 5 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 99 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 2801625422 + Number: 0 + - Name: '$SG73531' + Value: 0 + SectionNumber: 5 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: '$SG73539' + Value: 28 + SectionNumber: 5 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: '$SG73543' + Value: 68 + SectionNumber: 5 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: '.text$mn' + Value: 0 + SectionNumber: 6 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 335 + NumberOfRelocations: 17 + NumberOfLinenumbers: 0 + CheckSum: 2488225337 + Number: 0 + - Name: '.text$mn' + Value: 0 + SectionNumber: 7 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 25 + NumberOfRelocations: 2 + NumberOfLinenumbers: 0 + CheckSum: 210566957 + Number: 0 + Selection: IMAGE_COMDAT_SELECT_ANY + - Name: __imp__printf + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: '??1TestClass@@QAE@XZ' + Value: 0 + SectionNumber: 7 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: '?TestCPPEX@@YAXXZ' + Value: 0 + SectionNumber: 6 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: '?TestExceptions@@YAXXZ' + Value: 48 + SectionNumber: 6 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: '?foo@@YAXXZ' + Value: 80 + SectionNumber: 6 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _main + Value: 208 + SectionNumber: 6 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: __except_handler4 + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: '$LN5' + Value: 152 + SectionNumber: 6 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_LABEL + - Name: '$LN7' + Value: 157 + SectionNumber: 6 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_LABEL + - Name: '$LN6' + Value: 158 + SectionNumber: 6 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_LABEL + - Name: '$LN5' + Value: 285 + SectionNumber: 6 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_LABEL + - Name: '$LN7' + Value: 290 + SectionNumber: 6 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_LABEL + - Name: '$LN6' + Value: 291 + SectionNumber: 6 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_LABEL + - Name: '.xdata$x' + Value: 0 + SectionNumber: 8 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 60 + NumberOfRelocations: 4 + NumberOfLinenumbers: 0 + CheckSum: 2900129504 + Number: 0 + - Name: '__sehtable$?foo@@YAXXZ' + Value: 32 + SectionNumber: 8 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: '__sehtable$_main' + Value: 0 + SectionNumber: 8 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .rdata + Value: 0 + SectionNumber: 9 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 24 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 1296623929 + Number: 0 + Selection: IMAGE_COMDAT_SELECT_ANY + - Name: '??_C@_0BI@BBHGNMOG@Destroying?5TestClass?$CB?$AN?6?$AA@' + Value: 0 + SectionNumber: 9 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: ___security_cookie + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: .sxdata + Value: 0 + SectionNumber: 10 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 4 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 +... diff --git a/test/pecoff/Inputs/static-data1.obj.yaml b/test/pecoff/Inputs/static-data1.obj.yaml new file mode 100644 index 0000000000000..8dbe3e97eb51e --- /dev/null +++ b/test/pecoff/Inputs/static-data1.obj.yaml @@ -0,0 +1,67 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: "" + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: 03000000 + - Name: ".debug$S" + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ] + Alignment: 1 + SectionData: 04000000F1000000660000002B00011100000000433A5C63796777696E5C686F6D655C727569755C7374617469635C64617461312E6F626A0037003C1103020000030000000000000000000A0000001B9D01004D6963726F736F667420285229204D6163726F20417373656D626C657200000000 +symbols: + - Name: "@comp.id" + Value: 10394907 + SectionNumber: 65535 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 0 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: .data + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 4 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: ".debug$S" + Value: 0 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 116 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: _val1 + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/static-data2.obj.yaml b/test/pecoff/Inputs/static-data2.obj.yaml new file mode 100644 index 0000000000000..9b368c0338874 --- /dev/null +++ b/test/pecoff/Inputs/static-data2.obj.yaml @@ -0,0 +1,67 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: "" + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: 04000000 + - Name: ".debug$S" + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ] + Alignment: 1 + SectionData: 04000000F1000000660000002B00011100000000433A5C63796777696E5C686F6D655C727569755C7374617469635C64617461322E6F626A0037003C1103020000030000000000000000000A0000001B9D01004D6963726F736F667420285229204D6163726F20417373656D626C657200000000 +symbols: + - Name: "@comp.id" + Value: 10394907 + SectionNumber: 65535 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 0 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: .data + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 4 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: ".debug$S" + Value: 0 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 116 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: _val2 + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/static.lib b/test/pecoff/Inputs/static.lib Binary files differnew file mode 100644 index 0000000000000..5a631010e1d38 --- /dev/null +++ b/test/pecoff/Inputs/static.lib diff --git a/test/pecoff/Inputs/subsystem.main.yaml b/test/pecoff/Inputs/subsystem.main.yaml new file mode 100644 index 0000000000000..25fbe1be01439 --- /dev/null +++ b/test/pecoff/Inputs/subsystem.main.yaml @@ -0,0 +1,35 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: B82A000000C3 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 6 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: _main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _mainCRTStartup + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/subsystem.winmain.yaml b/test/pecoff/Inputs/subsystem.winmain.yaml new file mode 100644 index 0000000000000..7f0381e2dc69e --- /dev/null +++ b/test/pecoff/Inputs/subsystem.winmain.yaml @@ -0,0 +1,35 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: B82A000000C3 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 6 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: _WinMain + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _WinMainCRTStartup + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/tlsused.obj.yaml b/test/pecoff/Inputs/tlsused.obj.yaml new file mode 100644 index 0000000000000..6a7880fa78787 --- /dev/null +++ b/test/pecoff/Inputs/tlsused.obj.yaml @@ -0,0 +1,29 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [] +sections: + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: "0000000000000000" +symbols: + - Name: .data + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 8 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: __tls_used + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/unknown-drectve.obj.yaml b/test/pecoff/Inputs/unknown-drectve.obj.yaml new file mode 100644 index 0000000000000..79a12fe7bfa2c --- /dev/null +++ b/test/pecoff/Inputs/unknown-drectve.obj.yaml @@ -0,0 +1,42 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: 558BEC56FF15000000008B0D000000008B3103F0FF150000000003C65E5DC3 + - Name: .drectve + Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ] + Alignment: 2147483648 + + # /nosuchoption:foobar + SectionData: 2f6e6f737563686f7074696f6e3a666f6f62617200 + +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 31 + NumberOfRelocations: 3 + NumberOfLinenumbers: 0 + CheckSum: 3595596940 + Number: 0 + - Name: .drectve + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 13 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 +... diff --git a/test/pecoff/Inputs/unwind.obj.yaml b/test/pecoff/Inputs/unwind.obj.yaml new file mode 100644 index 0000000000000..2328cd5656013 --- /dev/null +++ b/test/pecoff/Inputs/unwind.obj.yaml @@ -0,0 +1,129 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 4883EC184889742410440F110424534889E3488D235B4883C418C3C34881ECF0FF00004881ECF0FF80004881C4F0FF80004881C4F0FF0000C3 + - Name: .xdata + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 0912080312030F300E880000096402000422001A000000000000000021000000000000001B000000000000000100000000000000010E06000E11F0FF80000701FE1F001A + Relocations: + - VirtualAddress: 20 + SymbolName: __C_specific_handler + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 32 + SymbolName: func + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 36 + SymbolName: func + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 40 + SymbolName: .xdata + Type: IMAGE_REL_AMD64_ADDR32NB + - Name: .pdata + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 000000001B0000000000000012000000120000001C00000000000000010000002C000000000000001D00000034000000 + Relocations: + - VirtualAddress: 0 + SymbolName: func + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 4 + SymbolName: func + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 8 + SymbolName: .xdata + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 12 + SymbolName: func + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 16 + SymbolName: func + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 20 + SymbolName: .xdata + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 24 + SymbolName: smallFunc + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 28 + SymbolName: smallFunc + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 32 + SymbolName: .xdata + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 36 + SymbolName: allocFunc + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 40 + SymbolName: allocFunc + Type: IMAGE_REL_AMD64_ADDR32NB + - VirtualAddress: 44 + SymbolName: .xdata + Type: IMAGE_REL_AMD64_ADDR32NB +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 57 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: .xdata + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 68 + NumberOfRelocations: 4 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 2 + - Name: .pdata + Value: 0 + SectionNumber: 3 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 48 + NumberOfRelocations: 12 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 3 + - Name: func + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: __C_specific_handler + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: smallFunc + Value: 27 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: allocFunc + Value: 28 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/vars-main-x64.obj.yaml b/test/pecoff/Inputs/vars-main-x64.obj.yaml new file mode 100644 index 0000000000000..c888c28e4a670 --- /dev/null +++ b/test/pecoff/Inputs/vars-main-x64.obj.yaml @@ -0,0 +1,63 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: 558BEC56FF15000000008B0D000000008B3103F0FF150000000003C65E5DC3 + Relocations: + - VirtualAddress: 6 + SymbolName: __imp_fn + Type: IMAGE_REL_AMD64_ADDR32 + - VirtualAddress: 12 + SymbolName: __imp_var + Type: IMAGE_REL_AMD64_ADDR32 + - VirtualAddress: 22 + SymbolName: __imp__name_with_underscore + Type: IMAGE_REL_AMD64_ADDR32 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 31 + NumberOfRelocations: 3 + NumberOfLinenumbers: 0 + CheckSum: 3595596940 + Number: 0 + - Name: __imp_fn + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: __imp__name_with_underscore + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: "__delayLoadHelper2" + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: __imp_var + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/vars-main-x86.obj.yaml b/test/pecoff/Inputs/vars-main-x86.obj.yaml new file mode 100644 index 0000000000000..fb016828df946 --- /dev/null +++ b/test/pecoff/Inputs/vars-main-x86.obj.yaml @@ -0,0 +1,69 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: 558BEC56FF15000000008B0D000000008B3103F0FF150000000003C65E5DC3 + Relocations: + - VirtualAddress: 6 + SymbolName: __imp__fn + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 12 + SymbolName: __imp__var + Type: IMAGE_REL_I386_DIR32 + - VirtualAddress: 22 + SymbolName: __imp___name_with_underscore + Type: IMAGE_REL_I386_DIR32 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 31 + NumberOfRelocations: 3 + NumberOfLinenumbers: 0 + CheckSum: 3595596940 + Number: 0 + - Name: __imp__fn + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: __imp___name_with_underscore + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: "___delayLoadHelper2@8" + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: __imp__var + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: ___ImageBase + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/Inputs/vars-main.c b/test/pecoff/Inputs/vars-main.c new file mode 100644 index 0000000000000..d588ca54b88e5 --- /dev/null +++ b/test/pecoff/Inputs/vars-main.c @@ -0,0 +1,7 @@ +__declspec(dllimport) int var; +__declspec(dllimport) int fn(void); +__declspec(dllimport) int _name_with_underscore(void); + +int main() { + return var + fn() + _name_with_underscore(); +} diff --git a/test/pecoff/Inputs/vars.c b/test/pecoff/Inputs/vars.c new file mode 100644 index 0000000000000..ae2ec46d1f476 --- /dev/null +++ b/test/pecoff/Inputs/vars.c @@ -0,0 +1,20 @@ +// cl.exe /c vars.c +// link /dll /nodefaultlib /entry:dllmain /export:var,@1,NONAME,DATA \ +// /export:fn /export:_name_with_underscore vars.obj + +// will be exported by ordinal +int var = 3; + +// will be exported by name +int fn(void) { + return 4; +} + +// will be exported by name +int _name_with_underscore(void) { + return 5; +} + +int dllmain() { + return 1; +} diff --git a/test/pecoff/Inputs/vars.dll.yaml b/test/pecoff/Inputs/vars.dll.yaml new file mode 100644 index 0000000000000..06f65ced69338 --- /dev/null +++ b/test/pecoff/Inputs/vars.dll.yaml @@ -0,0 +1,19 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_32BIT_MACHINE, IMAGE_FILE_DLL ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 2147483648 + SectionData: 558BECB8040000005DC3CCCCCCCCCCCC558BECB8050000005DC3CCCCCCCCCCCC558BECB8010000005DC30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + - Name: .rdata + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 2147483648 + SectionData: 0000000050570852000000004020000001000000030000000200000028200000342000003C200000003000001010000000100000492000005F20000001000200766172732E646C6C005F6E616D655F776974685F756E64657273636F726500666E00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 2147483648 + SectionData: 0300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +symbols: +... diff --git a/test/pecoff/Inputs/vars.lib b/test/pecoff/Inputs/vars.lib Binary files differnew file mode 100644 index 0000000000000..2d3aa2af678ef --- /dev/null +++ b/test/pecoff/Inputs/vars.lib diff --git a/test/pecoff/Inputs/vars64.lib b/test/pecoff/Inputs/vars64.lib Binary files differnew file mode 100644 index 0000000000000..fb48c9ab141ef --- /dev/null +++ b/test/pecoff/Inputs/vars64.lib diff --git a/test/pecoff/Inputs/weak-externals.asm b/test/pecoff/Inputs/weak-externals.asm new file mode 100644 index 0000000000000..7a5e918b92e77 --- /dev/null +++ b/test/pecoff/Inputs/weak-externals.asm @@ -0,0 +1,25 @@ +.386 +.model flat + +;; val1 should be linked normally. no_such_symbol1 should be ignored. +extern _no_such_symbol1 : PROC +extern _val1 (_no_such_symbol1): PROC + +;; no_such_symbol2 should be linked as val2. +extern _val2 : PROC +extern _no_such_symbol2 (_val2) : PROC + +;; no_such_symbol3 should fail to link. +extern _no_such_symbol3 : PROC + +public _fn1 +.code +_fn1: + push ebp + mov ebp, esp + call _val1 + call _no_such_symbol2 + call _no_such_symbol3 + pop ebp + ret 0 +end _fn1 diff --git a/test/pecoff/Inputs/weak-externals.obj.yaml b/test/pecoff/Inputs/weak-externals.obj.yaml new file mode 100644 index 0000000000000..ee76936c53266 --- /dev/null +++ b/test/pecoff/Inputs/weak-externals.obj.yaml @@ -0,0 +1,91 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 558BECE800000000E800000000E8000000005DC3 + Relocations: + - VirtualAddress: 4 + SymbolName: _val1 + Type: IMAGE_REL_I386_REL32 + - VirtualAddress: 9 + SymbolName: _no_such_symbol2 + Type: IMAGE_REL_I386_REL32 + - VirtualAddress: 14 + SymbolName: _no_such_symbol3 + Type: IMAGE_REL_I386_REL32 + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: "" +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 20 + NumberOfRelocations: 3 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: .data + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 0 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: _no_such_symbol1 + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _val2 + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _no_such_symbol3 + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _val1 + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_WEAK_EXTERNAL + WeakExternal: + TagIndex: 4 + Characteristics: IMAGE_WEAK_EXTERN_SEARCH_LIBRARY + - Name: _no_such_symbol2 + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_WEAK_EXTERNAL + WeakExternal: + TagIndex: 5 + Characteristics: IMAGE_WEAK_EXTERN_SEARCH_LIBRARY + - Name: _fn1 + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... diff --git a/test/pecoff/alignment.test b/test/pecoff/alignment.test new file mode 100644 index 0000000000000..bdf8bbaa2a3ea --- /dev/null +++ b/test/pecoff/alignment.test @@ -0,0 +1,22 @@ +# RUN: yaml2obj %p/Inputs/alignment.obj.yaml > %t.obj +# +# RUN: lld -flavor link /out:%t.exe /subsystem:console /force \ +# RUN: /entry:start /opt:noref -- %t.obj +# RUN: llvm-readobj -sections %t.exe | FileCheck %s + +CHECK: Name: .bss (2E 62 73 73 00 00 00 00) +CHECK: RawDataSize: 0 + +CHECK: Name: .data (2E 64 61 74 61 00 00 00) +CHECK-NEXT: VirtualSize: 0x6 + +CHECK: Name: .text (2E 74 65 78 74 00 00 00) +CHECK-NEXT: VirtualSize: 0x1001 + +CHECK: Name: .yyy +CHECK-NEXT: VirtualSize: 0x2 +CHECK-NEXT: VirtualAddress: 0x5000 + +CHECK: Name: .zzz +CHECK-NEXT: VirtualSize: 0x2 +CHECK-NEXT: VirtualAddress: 0x8000 diff --git a/test/pecoff/alternatename.test b/test/pecoff/alternatename.test new file mode 100644 index 0000000000000..926a8eae2876a --- /dev/null +++ b/test/pecoff/alternatename.test @@ -0,0 +1,44 @@ +# REQUIRES: x86 + +# RUN: yaml2obj %p/Inputs/alternatename1.obj.yaml > %t1.obj +# RUN: yaml2obj %p/Inputs/alternatename2.obj.yaml > %t2.obj +# RUN: yaml2obj %p/Inputs/alternatename3.obj.yaml > %t3.obj +# +# RUN: lld -flavor link /force /out:%t1.exe /alternatename:_main=_foo \ +# RUN: /subsystem:console -- %t1.obj +# RUN: llvm-objdump -d %t1.exe | FileCheck -check-prefix=CHECK1 %s +# +# RUN: lld -flavor link /force /out:%t2.exe /alternatename:_main=_foo \ +# RUN: /subsystem:console -- %t1.obj %t2.obj +# RUN: llvm-objdump -d %t2.exe | FileCheck -check-prefix=CHECK2 %s +# +# RUN: lld -flavor link /force /out:%t3.exe /subsystem:console -- %t3.obj +# RUN: llvm-objdump -d %t3.exe | FileCheck -check-prefix=CHECK3 %s +# +# RUN: lld -flavor link /force /out:%t4.exe /alternatename:_main=_foo \ +# RUN: /alternatename:_xyz=_foo /subsystem:console -- %t1.obj +# RUN: llvm-objdump -d %t4.exe | FileCheck -check-prefix=CHECK4 %s + +CHECK1: nop +CHECK1-NEXT: nop +CHECK1-NEXT: nop +CHECK1-NEXT: nop +CHECK1-NOT: int3 + +CHECK2: int3 +CHECK2-NEXT: int3 +CHECK2-NEXT: int3 +CHECK2-NEXT: int3 +CHECK2-NOT: nop + +CHECK3: nop +CHECK3-NEXT: nop +CHECK3-NEXT: nop +CHECK3-NEXT: nop +CHECK3-NOT: int3 + +CHECK4: nop +CHECK4-NEXT: nop +CHECK4-NEXT: nop +CHECK4-NEXT: nop +CHECK4-NOT: int3 diff --git a/test/pecoff/armnt-ImageBase.test b/test/pecoff/armnt-ImageBase.test new file mode 100644 index 0000000000000..b4bf28c5eb81b --- /dev/null +++ b/test/pecoff/armnt-ImageBase.test @@ -0,0 +1,14 @@ +# RUN: yaml2obj -format coff -o %t.obj %p/Inputs/armnt-ImageBase.obj.yaml +# RUN: llvm-readobj -r %t.obj | FileCheck %s -check-prefix BEFORE +# RUN: lld -flavor link /out:%t.exe %t.obj /subsystem:console +# RUN: llvm-readobj -r %t.exe | FileCheck %s -check-prefix AFTER + +BEFORE: Relocations [ +BEFORE: Section {{.*}} .text { +BEFORE: 0x4 IMAGE_REL_ARM_ADDR32 __ImageBase +BEFORE: } +BEFORE: ] + +AFTER: Relocations [ +AFTER-NEXT: ] + diff --git a/test/pecoff/armnt-addr32-exec.test b/test/pecoff/armnt-addr32-exec.test new file mode 100644 index 0000000000000..be223a0e7e5d1 --- /dev/null +++ b/test/pecoff/armnt-addr32-exec.test @@ -0,0 +1,11 @@ +# RUN: yaml2obj -format coff -o %t.obj %p/Inputs/armnt-addr32-exec.obj.yaml +# RUN: llvm-objdump -s %t.obj | FileCheck %s -check-prefix BEFORE +# RUN: lld -flavor link /out:%t.exe /entry:function /subsystem:console %t.obj +# RUN: llvm-objdump -s %t.exe | FileCheck %s -check-prefix AFTER + +BEFORE: Contents of section .rdata: +BEFORE: 0000 00000000 + +AFTER: Contents of section .rdata: +AFTER: 1000 01204000 + diff --git a/test/pecoff/armnt-addr32.test b/test/pecoff/armnt-addr32.test new file mode 100644 index 0000000000000..716217d457580 --- /dev/null +++ b/test/pecoff/armnt-addr32.test @@ -0,0 +1,11 @@ +# RUN: yaml2obj -format coff -o %t.obj %p/Inputs/armnt-addr32.obj.yaml +# RUN: llvm-objdump -s %t.obj | FileCheck %s -check-prefix BEFORE +# RUN: lld -flavor link /entry:is /subsystem:console /out:%t.exe %t.obj +# RUN: llvm-objdump -s %t.exe | FileCheck %s -check-prefix AFTER + +BEFORE: Contents of section .rdata: +BEFORE: 0000 00000000 00000000 + +AFTER: Contents of section .rdata: +AFTER: 1000 00104000 00000000 + diff --git a/test/pecoff/armnt-address-of-entry-point.test b/test/pecoff/armnt-address-of-entry-point.test new file mode 100644 index 0000000000000..3013b230bbd15 --- /dev/null +++ b/test/pecoff/armnt-address-of-entry-point.test @@ -0,0 +1,6 @@ +# RUN: yaml2obj -format coff -o %t.obj %p/Inputs/executable.obj.yaml +# RUN: lld -flavor link /out:%t.exe %t.obj +# RUN: llvm-readobj -file-headers %t.exe | FileCheck %s + +# CHECK: AddressOfEntryPoint: 0x1001 + diff --git a/test/pecoff/armnt-blx23t.test b/test/pecoff/armnt-blx23t.test new file mode 100644 index 0000000000000..56639fa22be98 --- /dev/null +++ b/test/pecoff/armnt-blx23t.test @@ -0,0 +1,27 @@ +# REQUIRES: arm + +# RUN: yaml2obj -format coff -o %t.obj %p/Inputs/armnt-blx23t.obj.yaml +# RUN: llvm-objdump -d %t.obj | FileCheck %s -check-prefix BEFORE +# RUN: lld -flavor link /entry:function /subsystem:console /out:%t.exe %t.obj +# RUN: llvm-objdump -d %t.exe | FileCheck %s -check-prefix AFTER + +BEFORE: Disassembly of section .text: +BEFORE: 0: 70 47 bx lr +BEFORE: 2: 00 bf nop +BEFORE: 4: 2d e9 00 48 push.w {r11, lr} +BEFORE: 8: eb 46 mov r11, sp +BEFORE: a: 20 20 movs r0, #32 +BEFORE: c: 00 f0 00 f8 bl #0 +BEFORE: 10: 01 30 adds r0, #1 +BEFORE: 12: bd e8 00 88 pop.w {r11, pc} + +AFTER: Disassembly of section .text: +AFTER: 1000: 70 47 bx lr +AFTER: 1002: 00 bf nop +AFTER: 1004: 2d e9 00 48 push.w {r11, lr} +AFTER: 1008: eb 46 mov r11, sp +AFTER: 100a: 20 20 movs r0, #32 +AFTER: 100c: ff f7 f8 ff bl #-16 +AFTER: 1010: 01 30 adds r0, #1 +AFTER: 1012: bd e8 00 88 pop.w {r11, pc} + diff --git a/test/pecoff/armnt-branch24t.test b/test/pecoff/armnt-branch24t.test new file mode 100644 index 0000000000000..1a727ed7d7257 --- /dev/null +++ b/test/pecoff/armnt-branch24t.test @@ -0,0 +1,20 @@ +# REQUIRES: arm + +# RUN: yaml2obj -format coff -o %t.obj %p/Inputs/armnt-branch24t.obj.yaml +# RUN: llvm-objdump -d %t.obj | FileCheck %s -check-prefix BEFORE +# RUN: lld -flavor link /entry:function /subsystem:console /out:%t.exe %t.obj +# RUN: llvm-objdump -d %t.exe | FileCheck %s -check-prefix AFTER + +BEFORE: Disassembly of section .text: +BEFORE: 0: 70 47 bx lr +BEFORE: 2: 00 bf nop +BEFORE: 4: 20 20 movs r0, #32 +BEFORE: 6: 00 f0 00 b8 b.w #0 + +AFTER: Disassembly of section .text: +AFTER: .text: +AFTER: 1000: 70 47 bx lr +AFTER: 1002: 00 bf nop +AFTER: 1004: 20 20 movs r0, #32 +AFTER: 1006: ff f7 fb bf b.w #-10 + diff --git a/test/pecoff/armnt-exports.s b/test/pecoff/armnt-exports.s new file mode 100644 index 0000000000000..cb500bf02e078 --- /dev/null +++ b/test/pecoff/armnt-exports.s @@ -0,0 +1,28 @@ + +# void __declspec(dllexport) function() {} +# void _DllMainCRTStartup() {} + + .syntax unified + .thumb + .text + + .def function + .scl 2 + .type 32 + .endef + .global function + .align 2 + .thumb_func +function: + bx lr + + .def _DllMainCRTStartup + .scl 2 + .type 32 + .endef + .global _DllMainCRTStartup + .align 2 + .thumb_func +_DllMainCRTStartup + bx lr + diff --git a/test/pecoff/armnt-exports.test b/test/pecoff/armnt-exports.test new file mode 100644 index 0000000000000..f0aa3eabcacc1 --- /dev/null +++ b/test/pecoff/armnt-exports.test @@ -0,0 +1,10 @@ +# RUN: yaml2obj -format coff -o %t.obj %p/Inputs/armnt-exports.obj.yaml +# RUN: lld -flavor link /dll /def:%p/Inputs/armnt-exports.def /out:%t.dll %t.obj +# RUN: llvm-readobj -coff-exports %t.dll | FileCheck %s + +CHECK: Export { +CHECK: Ordinal: 1 +CHECK: Name: function +CHECK: RVA: 0x2001 +CHECK: } + diff --git a/test/pecoff/armnt-imports.test b/test/pecoff/armnt-imports.test new file mode 100644 index 0000000000000..596270909c528 --- /dev/null +++ b/test/pecoff/armnt-imports.test @@ -0,0 +1,11 @@ +# RUN: yaml2obj -format coff -o %t.obj %p/Inputs/armnt-import.obj.yaml +# RUN: lld -flavor link /out:%t.exe /subsystem:console %t.obj %p/Inputs/library.lib +# RUN: llvm-readobj -coff-imports %t.exe | FileCheck %s + +CHECK: Import { +CHECK: Name: library.dll +CHECK: ImportLookupTableRVA: 0x4000 +CHECK: ImportAddressTableRVA: 0x2000 +CHECK: Symbol: function (0) +CHECK: } + diff --git a/test/pecoff/armnt-mov32t-exec.test b/test/pecoff/armnt-mov32t-exec.test new file mode 100644 index 0000000000000..de4feffea0cc8 --- /dev/null +++ b/test/pecoff/armnt-mov32t-exec.test @@ -0,0 +1,21 @@ +# REQUIRES: arm + +# RUN: yaml2obj -format coff -o %t.obj %p/Inputs/armnt-mov32t-exec.obj.yaml +# RUN: llvm-objdump -d %t.obj | FileCheck %s -check-prefix BEFORE +# RUN: lld -flavor link /out:%t.exe /subsystem:console /entry:get_function %t.obj +# RUN: llvm-objdump -d %t.exe | FileCheck %s -check-prefix AFTER + +BEFORE: Disassembly of section .text: +BEFORE: 0: 70 47 bx lr +BEFORE: 2: 00 bf nop +BEFORE: 4: 40 f2 00 00 movw r0, #0 +BEFORE: 8: c0 f2 00 00 movt r0, #0 +BEFORE: c: 70 47 bx lr + +AFTER: Disassembly of section .text: +AFTER: 1000: 70 47 bx lr +AFTER: 1002: 00 bf nop +AFTER: 1004: 41 f2 01 00 movw r0, #4097 +AFTER: 1008: c0 f2 40 00 movt r0, #64 +AFTER: 100c: 70 47 bx lr + diff --git a/test/pecoff/armnt-movt32t.test b/test/pecoff/armnt-movt32t.test new file mode 100644 index 0000000000000..2ae47ef75846a --- /dev/null +++ b/test/pecoff/armnt-movt32t.test @@ -0,0 +1,17 @@ +# REQUIRES: arm + +# RUN: yaml2obj -format coff -o %t.obj %p/Inputs/armnt-mov32t.obj.yaml +# RUN: llvm-objdump -d %t.obj | FileCheck %s -check-prefix BEFORE +# RUN: lld -flavor link /entry:get_buffer /subsystem:console /out:%t.exe %t.obj +# RUN: llvm-objdump -d %t.exe | FileCheck %s -check-prefix AFTER + +BEFORE: Disassembly of section .text: +BEFORE: 0: 40 f2 00 00 movw r0, #0 +BEFORE: 4: c0 f2 00 00 movt r0, #0 +BEFORE: 8: 70 47 bx lr + +AFTER: Disassembly of section .text: +AFTER: 0: 41 f2 00 00 movw r0, #4096 +AFTER: 4: c0 f2 40 00 movt r0, #64 +AFTER: 8: 70 47 bx lr + diff --git a/test/pecoff/armnt.test b/test/pecoff/armnt.test new file mode 100644 index 0000000000000..1cf6cd8114a45 --- /dev/null +++ b/test/pecoff/armnt.test @@ -0,0 +1,6 @@ +# RUN: yaml2obj -format coff -o %t.obj %p/Inputs/armnt-obj.yaml +# RUN: lld -flavor link /out:%t.dll /subsystem:console /entry:main %t.obj +# RUN: llvm-readobj -sections %t.dll | FileCheck %s + +CHECK: Format: COFF-ARM + diff --git a/test/pecoff/associative.test b/test/pecoff/associative.test new file mode 100644 index 0000000000000..f998befd007e1 --- /dev/null +++ b/test/pecoff/associative.test @@ -0,0 +1,10 @@ +# RUN: yaml2obj %p/Inputs/associative1.obj.yaml > %t1.obj +# RUN: yaml2obj %p/Inputs/associative1.obj.yaml > %t2.obj +# RUN: yaml2obj %p/Inputs/associative3.obj.yaml > %t3.obj +# +# RUN: lld -flavor link /machine:x86 /subsystem:console /entry:main \ +# RUN: /out:%t.exe -- %t1.obj %t2.obj %t3.obj +# RUN: obj2yaml %t.exe | FileCheck %s + +CHECK: - Name: .CRT +CHECK: SectionData: '77777777' diff --git a/test/pecoff/base-reloc.test b/test/pecoff/base-reloc.test new file mode 100644 index 0000000000000..5bc83de4d1c23 --- /dev/null +++ b/test/pecoff/base-reloc.test @@ -0,0 +1,78 @@ +# RUN: yaml2obj %p/Inputs/basereloc.obj.yaml > %t.obj +# +# RUN: lld -flavor link /out:%t.exe /subsystem:console /force /opt:noref \ +# RUN: -- %t.obj +# RUN: llvm-readobj -coff-basereloc %t.exe | FileCheck %s --check-prefix=BASEREL +# +# RUN: lld -flavor link /out:%t2.exe /subsystem:console /force /fixed \ +# RUN: /opt:noref -- %t.obj +# RUN: llvm-readobj -coff-basereloc %t2.exe | FileCheck %s --check-prefix=NOBASEREL + +BASEREL: BaseReloc [ +BASEREL-NEXT: Entry { +BASEREL-NEXT: Type: HIGHLOW +BASEREL-NEXT: Address: 0x2007 +BASEREL-NEXT: } +BASEREL-NEXT: Entry { +BASEREL-NEXT: Type: HIGHLOW +BASEREL-NEXT: Address: 0x200C +BASEREL-NEXT: } +BASEREL-NEXT: Entry { +BASEREL-NEXT: Type: HIGHLOW +BASEREL-NEXT: Address: 0x201E +BASEREL-NEXT: } +BASEREL-NEXT: Entry { +BASEREL-NEXT: Type: ABSOLUTE +BASEREL-NEXT: Address: 0x2000 +BASEREL-NEXT: } +BASEREL-NEXT: Entry { +BASEREL-NEXT: Type: HIGHLOW +BASEREL-NEXT: Address: 0x3007 +BASEREL-NEXT: } +BASEREL-NEXT: Entry { +BASEREL-NEXT: Type: HIGHLOW +BASEREL-NEXT: Address: 0x300C +BASEREL-NEXT: } +BASEREL-NEXT: Entry { +BASEREL-NEXT: Type: HIGHLOW +BASEREL-NEXT: Address: 0x301E +BASEREL-NEXT: } +BASEREL-NEXT: Entry { +BASEREL-NEXT: Type: ABSOLUTE +BASEREL-NEXT: Address: 0x3000 +BASEREL-NEXT: } +BASEREL-NEXT: ] + +NOBASEREL: BaseReloc [ +NOBASEREL-NEXT: ] + +# RUN: lld -flavor link /out:%t3.exe /subsystem:console /force /opt:noref \ +# RUN: -- %t.obj +# RUN: llvm-readobj -file-headers -sections %t3.exe | FileCheck %s \ +# RUN: --check-prefix=BASEREL-HEADER +# +# RUN: lld -flavor link /out:%t4.exe /subsystem:console /force /opt:noref \ +# RUN: /fixed -- %t.obj +# RUN: llvm-readobj -file-headers %t4.exe | FileCheck %s \ +# RUN: --check-prefix=NOBASEREL-HEADER + +BASEREL-HEADER-NOT: IMAGE_FILE_RELOCS_STRIPPED + +NOBASEREL-HEADER: IMAGE_FILE_RELOCS_STRIPPED + +BASEREL-HEADER: BaseRelocationTableRVA: 0x4000 +BASEREL-HEADER: BaseRelocationTableSize: 0x20 +BASEREL-HEADER: Name: .reloc (2E 72 65 6C 6F 63 00 00) +BASEREL-HEADER-NEXT: VirtualSize: 0x20 +BASEREL-HEADER-NEXT: VirtualAddress: 0x4000 +BASEREL-HEADER-NEXT: RawDataSize: 512 +BASEREL-HEADER-NEXT: PointerToRawData: 0xA00 +BASEREL-HEADER-NEXT: PointerToRelocations: 0x0 +BASEREL-HEADER-NEXT: PointerToLineNumbers: 0x0 +BASEREL-HEADER-NEXT: RelocationCount: 0 +BASEREL-HEADER-NEXT: LineNumberCount: 0 +BASEREL-HEADER-NEXT: Characteristics [ (0x42000040) +BASEREL-HEADER-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA (0x40) +BASEREL-HEADER-NEXT: IMAGE_SCN_MEM_DISCARDABLE (0x2000000) +BASEREL-HEADER-NEXT: IMAGE_SCN_MEM_READ (0x40000000) +BASEREL-HEADER-NEXT: ] diff --git a/test/pecoff/baseaddr.test b/test/pecoff/baseaddr.test new file mode 100644 index 0000000000000..dbd091bc2e30d --- /dev/null +++ b/test/pecoff/baseaddr.test @@ -0,0 +1,18 @@ +# RUN: yaml2obj %p/Inputs/nop.obj.yaml > %t.obj +# +# RUN: lld -flavor link /out:%t1.exe /opt:noref /subsystem:console /force \ +# RUN: -- %t.obj +# RUN: llvm-readobj -file-headers %t1.exe | FileCheck -check-prefix=DEFAULT %s +# +# RUN: lld -flavor link /out:%t2.exe /opt:noref /base:8388608 \ +# RUN: /subsystem:console /force -- %t.obj +# RUN: llvm-readobj -file-headers %t2.exe | FileCheck -check-prefix=BASE %s + +DEFAULT: ImageBase: 0x400000 + +BASE: ImageBase: 0x800000 + +# RUN: not lld -flavor link /base:3 /subsystem:console -- %t.obj >& %t.log +# RUN: FileCheck -check-prefix=ERROR %s < %t.log + +ERROR: Base address have to be multiple of 64K, but got 3 diff --git a/test/pecoff/bss-section.test b/test/pecoff/bss-section.test new file mode 100644 index 0000000000000..4181e994fbf54 --- /dev/null +++ b/test/pecoff/bss-section.test @@ -0,0 +1,21 @@ +# RUN: lld -flavor link /out:%t.exe /subsystem:console /force \ +# RUN: -- %p/Inputs/bss.obj +# RUN: llvm-readobj -sections %t.exe | FileCheck %s + +CHECK: Section { +CHECK: Number: 1 +CHECK-NEXT: Name: .bss +CHECK-NEXT: VirtualSize: 0x320 +CHECK-NEXT: VirtualAddress: 0x1000 +CHECK-NEXT: RawDataSize: 0 +CHECK-NEXT: PointerToRawData: 0x0 +CHECK-NEXT: PointerToRelocations: 0x0 +CHECK-NEXT: PointerToLineNumbers: 0x0 +CHECK-NEXT: RelocationCount: 0 +CHECK-NEXT: LineNumberCount: 0 +CHECK-NEXT: Characteristics [ +CHECK-NEXT: IMAGE_SCN_CNT_UNINITIALIZED_DATA +CHECK-NEXT: IMAGE_SCN_MEM_READ +CHECK-NEXT: IMAGE_SCN_MEM_WRITE +CHECK-NEXT: ] +CHECK-NEXT: } diff --git a/test/pecoff/comdat.test b/test/pecoff/comdat.test new file mode 100644 index 0000000000000..d752309d75152 --- /dev/null +++ b/test/pecoff/comdat.test @@ -0,0 +1,12 @@ +# RUN: yaml2obj %p/Inputs/comdat.obj.yaml > %t1.obj +# RUN: yaml2obj %p/Inputs/comdat.obj.yaml > %t2.obj +# +# RUN: lld -flavor link /out:%t.exe /subsystem:console /opt:noref /force \ +# RUN: -- %t1.obj %t2.obj 2>&1 > %t.log +# +# FileCheck complains if the input files is empty, so add a dummy line. +# RUN: echo foo >> %t.log +# +# RUN: FileCheck %s < %t.log + +CHECK-NOT: duplicate symbol error diff --git a/test/pecoff/common-symbol.test b/test/pecoff/common-symbol.test new file mode 100644 index 0000000000000..49d4d8725da41 --- /dev/null +++ b/test/pecoff/common-symbol.test @@ -0,0 +1,14 @@ +# REQUIRES: x86 + +# RUN: yaml2obj %p/Inputs/common-symbol.obj.yaml > %t.obj +# +# RUN: lld -flavor link /machine:x64 /out:%t.exe /subsystem:console /force \ +# RUN: /opt:noref -- %t.obj %t.obj +# RUN: llvm-objdump -d %t.exe | FileCheck %s + +# Operands of B8 (MOV EAX) are common symbols +CHECK: 3000: b8 00 10 00 40 +CHECK: 3005: b8 04 10 00 40 +CHECK: 300a: b8 20 10 00 40 +CHECK: 300f: b8 60 10 00 40 +CHECK: 3014: b8 80 10 00 40 diff --git a/test/pecoff/conflicting-machine.test b/test/pecoff/conflicting-machine.test new file mode 100644 index 0000000000000..6c71521fe1b7f --- /dev/null +++ b/test/pecoff/conflicting-machine.test @@ -0,0 +1,6 @@ +# RUN: yaml2obj %p/Inputs/vars-main-x64.obj.yaml > %t-x64.obj + +# RUN: not lld -flavor link /machine:x86 /out:%t.exe /entry:main %t-x64.obj 2>&1 \ +# RUN: | FileCheck %s + +CHECK: module machine type 'X64' conflicts with target machine type 'X86' diff --git a/test/pecoff/delayimport.test b/test/pecoff/delayimport.test new file mode 100644 index 0000000000000..89ceb4ad5b20a --- /dev/null +++ b/test/pecoff/delayimport.test @@ -0,0 +1,54 @@ +# RUN: yaml2obj %p/Inputs/vars-main-x86.obj.yaml > %t-x86.obj +# RUN: yaml2obj %p/Inputs/vars-main-x64.obj.yaml > %t-x64.obj +# +# RUN: lld -flavor link /out:%t1.exe /subsystem:console /entry:main \ +# RUN: /delayload:vars.dll -- %t-x86.obj %p/Inputs/vars.lib +# RUN: llvm-readobj -coff-imports %t1.exe | FileCheck -check-prefix=X86 %s +# +# RUN: lld -flavor link /out:%t2.exe /subsystem:console /entry:main \ +# RUN: /machine:x64 /delayload:vars64.dll -- %t-x64.obj %p/Inputs/vars64.lib +# RUN: llvm-readobj -coff-imports %t2.exe | FileCheck -check-prefix=X64 %s + +X86: DelayImport { +X86-NEXT: Name: vars.dll +X86-NEXT: Attributes: 0x1 +X86-NEXT: ModuleHandle: 0x1000 +X86-NEXT: ImportAddressTable: 0x1004 +X86-NEXT: ImportNameTable: 0x2000 +X86-NEXT: BoundDelayImportTable: 0x0 +X86-NEXT: UnloadDelayImportTable: 0x0 +X86-NEXT: Import { +X86-NEXT: Symbol: _name_with_underscore (0) +X86-NEXT: Address: 0x40501F +X86-NEXT: } +X86-NEXT: Import { +X86-NEXT: Symbol: fn (1) +X86-NEXT: Address: 0x405034 +X86-NEXT: } +X86-NEXT: Import { +X86-NEXT: Symbol: (1) +X86-NEXT: Address: 0x405049 +X86-NEXT: } +X86-NEXT: } + +X64: DelayImport { +X64-NEXT: Name: vars64.dll +X64-NEXT: Attributes: 0x1 +X64-NEXT: ModuleHandle: 0x1000 +X64-NEXT: ImportAddressTable: 0x1008 +X64-NEXT: ImportNameTable: 0x2000 +X64-NEXT: BoundDelayImportTable: 0x0 +X64-NEXT: UnloadDelayImportTable: 0x0 +X64-NEXT: Import { +X64-NEXT: Symbol: _name_with_underscore (0) +X64-NEXT: Address: 0x14000501F +X64-NEXT: } +X64-NEXT: Import { +X64-NEXT: Symbol: fn (1) +X64-NEXT: Address: 0x140005076 +X64-NEXT: } +X64-NEXT: Import { +X64-NEXT: Symbol: (1) +X64-NEXT: Address: 0x1400050CD +X64-NEXT: } +X64-NEXT: } diff --git a/test/pecoff/dll.test b/test/pecoff/dll.test new file mode 100644 index 0000000000000..666e9f1152b04 --- /dev/null +++ b/test/pecoff/dll.test @@ -0,0 +1,7 @@ +# RUN: yaml2obj %p/Inputs/nop.obj.yaml > %t.obj +# +# RUN: lld -flavor link /out:%t.exe /subsystem:console \ +# RUN: /entry:start /dll -- %t.obj +# RUN: llvm-readobj -file-headers %t.exe | FileCheck %s + +CHECK: IMAGE_FILE_DLL (0x2000) diff --git a/test/pecoff/dosstub.test b/test/pecoff/dosstub.test new file mode 100644 index 0000000000000..f0458501704df --- /dev/null +++ b/test/pecoff/dosstub.test @@ -0,0 +1,11 @@ +# RUN: yaml2obj %p/Inputs/nop.obj.yaml > %t.obj + +# RUN: echo "MZ Hello world" > %t.stub +# RUN: lld -flavor link /out:%t.exe /entry:start /subsystem:console \ +# RUN: /stub:%t.stub -- %t.obj +# RUN: FileCheck -check-prefix=FILE %s < %t.exe +# RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=READOBJ %s + +FILE: MZ Hello world + +READOBJ: Format: COFF-i386 diff --git a/test/pecoff/drectve.test b/test/pecoff/drectve.test new file mode 100644 index 0000000000000..258f608e5dff6 --- /dev/null +++ b/test/pecoff/drectve.test @@ -0,0 +1,39 @@ +# Test if the linker can properly parse the .drectve section contents. +# "drectve.obj" contains "/defaultlib:vars /subsystem:console,42.195 -?foo" +# in its .drectve section. + +# RUN: yaml2obj %p/Inputs/drectve.obj.yaml > %t.obj +# +# RUN: lld -flavor link /out:%t.exe /entry:main /opt:noref /libpath:%p/Inputs \ +# RUN: -- %t.obj >& %t.log +# +# RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=HEADER %s +# RUN: llvm-objdump -p %t.exe | FileCheck -check-prefix=IMPORT %s +# RUN: echo >> %t.log +# RUN: FileCheck -check-prefix=ERROR %s < %t.log + +HEADER: MajorOperatingSystemVersion: 42 +HEADER: MinorOperatingSystemVersion: 195 + +IMPORT: DLL Name: vars.dll +IMPORT-NEXT: Hint/Ord Name +IMPORT-NEXT: 0 _name_with_underscore +IMPORT-NEXT: 1 fn +IMPORT-NEXT: 1 + +ERROR-NOT: foo + + +# drectve2.obj contains "/include:foo". +# RUN: yaml2obj %p/Inputs/drectve2.obj.yaml > %t2.obj +# RUN: not lld -flavor link /out:%t2.exe /entry:main -- %t2.obj >& %t2.log +# RUN: FileCheck -check-prefix=UNDEF2 %s < %t2.log + +UNDEF2: Undefined symbol: {{.*}}: foo + +# drectve4.lib contains "/include:bar". +# RUN: not lld -flavor link /force /out:%t3.exe /entry:main /include:_fn1 -- \ +# RUN: %t2.obj %p/Inputs/drectve3.lib >& %t3.log +# RUN: FileCheck -check-prefix=UNDEF3 %s < %t3.log + +UNDEF3: Undefined symbol: {{.*}}: bar diff --git a/test/pecoff/dynamic.test b/test/pecoff/dynamic.test new file mode 100644 index 0000000000000..6b9a945b1fc50 --- /dev/null +++ b/test/pecoff/dynamic.test @@ -0,0 +1,11 @@ +# RUN: yaml2obj %p/Inputs/vars-main-x86.obj.yaml > %t.obj +# +# RUN: lld -flavor link /out:%t.exe /subsystem:console /entry:main /opt:noref \ +# RUN: -- %t.obj %p/Inputs/vars.lib +# RUN: llvm-objdump -p %t.exe | FileCheck %s + +CHECK: DLL Name: vars.dll +CHECK-NEXT: Hint/Ord Name +CHECK-NEXT: 0 _name_with_underscore +CHECK-NEXT: 1 fn +CHECK-NEXT: 1 diff --git a/test/pecoff/dynamicbase.test b/test/pecoff/dynamicbase.test new file mode 100644 index 0000000000000..9ed795b99db99 --- /dev/null +++ b/test/pecoff/dynamicbase.test @@ -0,0 +1,24 @@ +# RUN: yaml2obj %p/Inputs/hello.obj.yaml > %t.obj +# +# RUN: lld -flavor link /out:%t1.exe /subsystem:console /force -- %t.obj +# RUN: llvm-readobj -file-headers %t1.exe | FileCheck %s \ +# RUN: --check-prefix=DYNAMICBASE +# +# RUN: lld -flavor link /out:%t2.exe /subsystem:console /force /dynamicbase:no \ +# RUN: -- %t.obj +# RUN: llvm-readobj -file-headers %t2.exe | FileCheck %s \ +# RUN: --check-prefix=NODYNAMICBASE +# +# RUN: lld -flavor link /out:%t3.exe /subsystem:console /force /fixed -- %t.obj +# RUN: llvm-readobj -file-headers %t3.exe | FileCheck %s \ +# RUN: --check-prefix=NODYNAMICBASE +# +# RUN: not lld -flavor link /out:%t4.exe /subsystem:console /force /fixed \ +# RUN: /dynamicbase -- %t.obj 2> %t.err +# RUN: FileCheck %s --check-prefix=DYNAMIC-AND-FIXED < %t.err + +DYNAMICBASE: IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE + +NODYNAMICBASE-NOT: IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE + +DYNAMIC-AND-FIXED: /dynamicbase must not be specified with /fixed diff --git a/test/pecoff/entry.test b/test/pecoff/entry.test new file mode 100644 index 0000000000000..b48e5a038293e --- /dev/null +++ b/test/pecoff/entry.test @@ -0,0 +1,41 @@ +# REQUIRES: asserts + +# RUN: yaml2obj %p/Inputs/entry.obj.yaml > %t.obj + +# RUN: not lld -flavor link /out:%t.exe /alternatename:_main=_foo \ +# RUN: -- %t.obj 2> %t.log +# RUN: FileCheck -check-prefix=MAIN %s < %t.log + +MAIN: _mainCRTStartup + +# RUN: not lld -flavor link /out:%t.exe /alternatename:_wmain=_foo \ +# RUN: -- %t.obj 2> %t.log +# RUN: FileCheck -check-prefix=WMAIN %s < %t.log + +WMAIN: _wmainCRTStartup + +# RUN: not lld -flavor link /out:%t.exe /alternatename:_WinMain=_foo \ +# RUN: -- %t.obj 2> %t.log +# RUN: FileCheck -check-prefix=WINMAIN %s < %t.log +# RUN: not lld -flavor link /out:%t.exe /alternatename:_WinMain@16=_foo \ +# RUN: -- %t.obj 2> %t.log +# RUN: FileCheck -check-prefix=WINMAIN %s < %t.log + +WINMAIN: _WinMainCRTStartup + +# RUN: not lld -flavor link /out:%t.exe /alternatename:_wWinMain=_foo \ +# RUN: -- %t.obj 2> %t.log +# RUN: FileCheck -check-prefix=WWINMAIN %s < %t.log + +WWINMAIN: _wWinMainCRTStartup + +# RUN: lld -flavor link /out:%t.exe /alternatename:_main=_foo \ +# RUN: /alternatename:_mainCRTStartup=_bar -- %t.obj +# RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=MAINADDR %s + +MAINADDR: AddressOfEntryPoint: 0x1004 + +# RUN: lld -flavor link /out:%t.exe /subsystem:console /entry:baz -- %t.obj +# RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=MANGLE %s + +MANGLE: AddressOfEntryPoint: 0x1004 diff --git a/test/pecoff/export-warning.test b/test/pecoff/export-warning.test new file mode 100644 index 0000000000000..5c7647de00bd2 --- /dev/null +++ b/test/pecoff/export-warning.test @@ -0,0 +1,19 @@ +# RUN: yaml2obj %p/Inputs/export.obj.yaml > %t.obj +# +# RUN: lld -flavor link /out:%t1.dll /dll /entry:init \ +# RUN: /export:exportfn1 /export:exportfn1 -- %t.obj 2> %t1.log +# RUN: echo >> %t1.log +# RUN: FileCheck -check-prefix=CHECK1 %s < %t1.log +CHECK1-NOT: Export symbol '_exportfn1' specified more than once. + +# RUN: lld -flavor link /out:%t2.dll /dll /entry:init \ +# RUN: /export:exportfn1 /export:exportfn1,@5 -- %t.obj 2> %t2.log +# RUN: echo >> %t2.log +# RUN: FileCheck -check-prefix=CHECK2 %s < %t2.log +CHECK2: Export symbol '_exportfn1' specified more than once. + +# RUN: lld -flavor link /out:%t3.dll /dll /entry:init \ +# RUN: /export:exportfn1,@8 /export:exportfn1,@5 -- %t.obj 2> %t3.log +# RUN: echo >> %t3.log +# RUN: FileCheck -check-prefix=CHECK3 %s < %t3.log +CHECK3: Export symbol '_exportfn1' specified more than once. diff --git a/test/pecoff/export.test b/test/pecoff/export.test new file mode 100644 index 0000000000000..63b8677cd4b78 --- /dev/null +++ b/test/pecoff/export.test @@ -0,0 +1,90 @@ +# RUN: yaml2obj %p/Inputs/export.obj.yaml > %t.obj +# +# RUN: lld -flavor link /out:%t1.dll /dll /entry:init \ +# RUN: /export:exportfn1 /export:exportfn2 -- %t.obj +# RUN: llvm-objdump -p %t1.dll | FileCheck -check-prefix=CHECK1 %s + +CHECK1: Export Table: +CHECK1: DLL name: export.test.tmp1.dll +CHECK1: Ordinal RVA Name +CHECK1-NEXT: 1 0x2008 exportfn1 +CHECK1-NEXT: 2 0x2010 exportfn2 + +# RUN: lld -flavor link /out:%t2.dll /dll /subsystem:console /entry:init \ +# RUN: /export:exportfn1,@5 /export:exportfn2 -- %t.obj +# RUN: llvm-objdump -p %t2.dll | FileCheck -check-prefix=CHECK2 %s + +CHECK2: Export Table: +CHECK2: DLL name: export.test.tmp2.dll +CHECK2: Ordinal RVA Name +CHECK2-NEXT: 5 0x2008 exportfn1 +CHECK2-NEXT: 6 0x2010 exportfn2 + +# RUN: lld -flavor link /out:%t3.dll /dll /subsystem:console /entry:init \ +# RUN: /export:exportfn1,@5,noname /export:exportfn2 -- %t.obj +# RUN: llvm-objdump -p %t3.dll | FileCheck -check-prefix=CHECK3 %s + +CHECK3: Export Table: +CHECK3: DLL name: export.test.tmp3.dll +CHECK3: Ordinal RVA Name +CHECK3-NEXT: 5 0x2008 +CHECK3-NEXT: 6 0x2010 exportfn2 + +# RUN: lld -flavor link /out:%t4.dll /dll /entry:init \ +# RUN: /def:%p/Inputs/exports.def -- %t.obj +# RUN: llvm-objdump -p %t4.dll | FileCheck -check-prefix=CHECK4 %s + +CHECK4: Export Table: +CHECK4: DLL name: export.test.tmp4.dll +CHECK4: Ordinal RVA Name +CHECK4-NEXT: 5 0x2008 exportfn1 +CHECK4-NEXT: 6 0x2010 exportfn2 +CHECK4-NEXT: 7 0x2010 exportfn3@256 +CHECK4-NEXT: 8 0x2010 exportfn5 + +# RUN: lld -flavor link /out:%t5.dll /dll /entry:init \ +# RUN: /export:exportfn7 -- %t.obj +# RUN: llvm-objdump -p %t5.dll | FileCheck -check-prefix=CHECK5 %s + +CHECK5: Export Table: +CHECK5: DLL name: export.test.tmp5.dll +CHECK5: Ordinal RVA Name +CHECK5-NEXT: 1 0x2010 exportfn3@256 +CHECK5-NEXT: 2 0x2010 exportfn7 + +# RUN: lld -flavor link /out:%t6.dll /dll /entry:init \ +# RUN: /export:exportfn8 -- %t.obj +# RUN: llvm-objdump -p %t6.dll | FileCheck -check-prefix=CHECK6 %s + +CHECK6: Export Table: +CHECK6: DLL name: export.test.tmp6.dll +CHECK6: Ordinal RVA Name +CHECK6-NEXT: 1 0x2010 exportfn3@256 +CHECK6-NEXT: 2 0x2010 exportfn8 + +# RUN: lld -flavor link /out:%t7.dll /dll /entry:init \ +# RUN: /export:exportfn7 /export:exportfn7@8 \ +# RUN: /export:exportfn8 /export:exportfn8 /export:exportfn3 -- %t.obj +# RUN: llvm-objdump -p %t7.dll | FileCheck -check-prefix=DUP %s + +DUP: Export Table: +DUP: DLL name: export.test.tmp7.dll +DUP: Ordinal RVA Name +DUP-NEXT: 1 0x2010 exportfn3 +DUP-NEXT: 2 0x2010 exportfn7 +DUP-NEXT: 3 0x2010 exportfn8 +DUP-NOT: ?exportfn8@@YAXXZ +DUP-NOT: exportfn3@256 + +# RUN: yaml2obj %p/Inputs/export.obj.yaml > %t.obj +# +# RUN: lld -flavor link /out:%t8.dll /dll /entry:init \ +# RUN: /export:f1=exportfn1 /export:f2@4=exportfn2,private -- %t.obj +# RUN: llvm-objdump -p %t8.dll | FileCheck -check-prefix=EQUAL %s + +EQUAL: Export Table: +EQUAL: DLL name: export.test.tmp8.dll +EQUAL: Ordinal RVA Name +EQUAL-NEXT: 1 0x2010 exportfn3@256 +EQUAL-NEXT: 2 0x2008 f1 +EQUAL-NEXT: 3 0x2010 f2{{$}} diff --git a/test/pecoff/exportlib.test b/test/pecoff/exportlib.test new file mode 100644 index 0000000000000..b65751cfebd77 --- /dev/null +++ b/test/pecoff/exportlib.test @@ -0,0 +1,32 @@ +# REQUIRES: winlib + +# RUN: yaml2obj %p/Inputs/export.obj.yaml > %t.obj +# +# RUN: lld -flavor link /out:%t.dll /dll /entry:init \ +# RUN: /export:exportfn1 /export:exportfn2 -- %t.obj +# RUN: llvm-readobj %t.lib | FileCheck %s + +CHECK: File: exportlib.test.tmp.dll +CHECK: Format: COFF-i386 +CHECK: Arch: i386 +CHECK: AddressSize: 32bit + +CHECK: File: exportlib.test.tmp.dll +CHECK: Format: COFF-i386 +CHECK: Arch: i386 +CHECK: AddressSize: 32bit + +CHECK: File: exportlib.test.tmp.dll +CHECK: Format: COFF-i386 +CHECK: Arch: i386 +CHECK: AddressSize: 32bit + +CHECK: File: exportlib.test.tmp.dll +CHECK: Format: COFF-<unknown arch> +CHECK: Arch: unknown +CHECK: AddressSize: 32bit + +CHECK: File: exportlib.test.tmp.dll +CHECK: Format: COFF-<unknown arch> +CHECK: Arch: unknown +CHECK: AddressSize: 32bit diff --git a/test/pecoff/exportlib2.test b/test/pecoff/exportlib2.test new file mode 100644 index 0000000000000..e846b0bdd064a --- /dev/null +++ b/test/pecoff/exportlib2.test @@ -0,0 +1,21 @@ +# RUN: yaml2obj %p/Inputs/export.obj.yaml > %t.obj +# +# RUN: lld -flavor link /out:%t.dll /dll /entry:init \ +# RUN: /export:exportfn1 /export:exportfn2 /lldmoduledeffile:%t1.def -- %t.obj +# RUN: FileCheck -check-prefix=CHECK1 %s < %t1.def + +CHECK1: LIBRARY "exportlib2.test.tmp.dll" +CHECK1: EXPORTS +CHECK1: exportfn1 @1 +CHECK1: exportfn2 @2 +CHECK1: exportfn3@256 @3 + +# RUN: lld -flavor link /out:%t.dll /dll /entry:init \ +# RUN: /def:%p/Inputs/exports2.def /lldmoduledeffile:%t2.def -- %t.obj +# RUN: FileCheck -check-prefix=CHECK2 %s < %t2.def + +CHECK2: LIBRARY "exportlib2.test.tmp.dll" +CHECK2: EXPORTS +CHECK2: exportfn1 @5 +CHECK2: exportfn3@256 @6 +CHECK2: exportfn7@8 @7 diff --git a/test/pecoff/grouped-sections.test b/test/pecoff/grouped-sections.test new file mode 100644 index 0000000000000..40ae1478997e6 --- /dev/null +++ b/test/pecoff/grouped-sections.test @@ -0,0 +1,17 @@ +# RUN: yaml2obj %p/Inputs/grouped-sections.obj.yaml > %t.obj +# +# RUN: lld -flavor link /out:%t.exe /subsystem:console /entry:main -- %t.obj +# RUN: llvm-objdump -s %t.exe | FileCheck %s +# +# The file "grouped-sections.obj" has three data sections in the following +# order: +# +# .data$2 +# .data$1 +# .data +# +# If all the sections will be merged correctly, the resulting ".data" +# section will have the string "Hello, world". + +CHECK: Contents of section .data: +CHECK-NEXT: Hello, world diff --git a/test/pecoff/hello.test b/test/pecoff/hello.test new file mode 100644 index 0000000000000..679b8b3ad9848 --- /dev/null +++ b/test/pecoff/hello.test @@ -0,0 +1,51 @@ +# RUN: yaml2obj %p/Inputs/hello.obj.yaml > %t.obj + +# RUN: lld -flavor link /out:%t1.exe /subsystem:console /force -- %t.obj +# RUN: llvm-readobj -file-headers %t1.exe | FileCheck -check-prefix=FILE %s + +FILE: ImageOptionalHeader { +FILE: SizeOfInitializedData: 1024 +FILE: SizeOfHeaders: 512 +FILE: } + +# RUN: lld -flavor link /out:%t2.exe /subsystem:console /force -- %t.obj +# RUN: llvm-readobj -sections %t2.exe | FileCheck -check-prefix=SECTIONS %s + +SECTIONS: Format: COFF-i386 +SECTIONS-NEXT: Arch: i386 +SECTIONS-NEXT: AddressSize: 32bit +SECTIONS-NEXT: Sections [ +SECTIONS-NEXT: Section { +SECTIONS-NEXT: Number: 1 +SECTIONS-NEXT: Name: .data +SECTIONS-NEXT: VirtualSize: 0x12 +SECTIONS-NEXT: VirtualAddress: 0x1000 +SECTIONS-NEXT: RawDataSize: 512 +SECTIONS-NEXT: PointerToRawData: 0x200 +SECTIONS-NEXT: PointerToRelocations: 0x0 +SECTIONS-NEXT: PointerToLineNumbers: 0x0 +SECTIONS-NEXT: RelocationCount: 0 +SECTIONS-NEXT: LineNumberCount: 0 +SECTIONS-NEXT: Characteristics [ +SECTIONS-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA +SECTIONS-NEXT: IMAGE_SCN_MEM_READ +SECTIONS-NEXT: IMAGE_SCN_MEM_WRITE +SECTIONS-NEXT: ] +SECTIONS-NEXT: } +SECTIONS-NEXT: Section { +SECTIONS-NEXT: Number: 2 +SECTIONS-NEXT: Name: .text (2E 74 65 78 74 00 00 00) +SECTIONS-NEXT: VirtualSize: 0x1C +SECTIONS-NEXT: VirtualAddress: 0x2000 +SECTIONS-NEXT: RawDataSize: 512 +SECTIONS-NEXT: PointerToRawData: 0x400 +SECTIONS-NEXT: PointerToRelocations: 0x0 +SECTIONS-NEXT: PointerToLineNumbers: 0x0 +SECTIONS-NEXT: RelocationCount: 0 +SECTIONS-NEXT: LineNumberCount: 0 +SECTIONS-NEXT: Characteristics [ +SECTIONS-NEXT: IMAGE_SCN_CNT_CODE +SECTIONS-NEXT: IMAGE_SCN_MEM_EXECUTE +SECTIONS-NEXT: IMAGE_SCN_MEM_READ +SECTIONS-NEXT: ] +SECTIONS-NEXT: } diff --git a/test/pecoff/hello64.test b/test/pecoff/hello64.test new file mode 100644 index 0000000000000..7536e5a8fd266 --- /dev/null +++ b/test/pecoff/hello64.test @@ -0,0 +1,22 @@ +# REQUIRES: x86 + +# RUN: yaml2obj %p/Inputs/hello64.obj.yaml > %t.obj + +# RUN: lld -flavor link /out:%t.exe /subsystem:windows /machine:x64 \ +# RUN: /entry:main -- %t.obj %p/Inputs/hello64lib.lib +# RUN: llvm-objdump -disassemble %t.exe | FileCheck %s + +CHECK: 6000: 48 83 ec 28 subq $40, %rsp +CHECK: 6004: 48 c7 c1 00 00 00 00 movq $0, %rcx +CHECK: 600b: 48 8d 15 f4 af ff ff leaq -20492(%rip), %rdx +CHECK: 6012: 4c 8d 05 e7 af ff ff leaq -20505(%rip), %r8 +CHECK: 6019: 41 b9 00 00 00 00 movl $0, %r9d +CHECK: 601f: e8 12 00 00 00 callq 18 +CHECK: 6024: b9 00 00 00 00 movl $0, %ecx +CHECK: 6029: e8 00 00 00 00 callq 0 +CHECK: 602e: ff 25 cc cf ff ff jmpq *-12340(%rip) +CHECK: 6034: cc int3 +CHECK: 6035: cc int3 +CHECK: 6036: ff 25 cc cf ff ff jmpq *-12340(%rip) +CHECK: 603c: cc int3 +CHECK: 603d: cc int3 diff --git a/test/pecoff/help.test b/test/pecoff/help.test new file mode 100644 index 0000000000000..f5e9c358f1a8f --- /dev/null +++ b/test/pecoff/help.test @@ -0,0 +1,4 @@ +# RUN: not lld -flavor link /help | FileCheck %s +# RUN: not lld -flavor link '/?' | FileCheck %s + +CHECK: USAGE diff --git a/test/pecoff/imagebase.test b/test/pecoff/imagebase.test new file mode 100644 index 0000000000000..bb83e6b661240 --- /dev/null +++ b/test/pecoff/imagebase.test @@ -0,0 +1,15 @@ +# REQUIRES: x86 + +# RUN: yaml2obj %p/Inputs/imagebase.obj.yaml > %t.obj + +# RUN: lld -flavor link /out:%t1.exe /subsystem:console /entry:_start \ +# RUN: /opt:noref -- %t.obj +# RUN: llvm-objdump -disassemble %t1.exe | FileCheck -check-prefix=DEFAULT %s + +DEFAULT: a1 00 00 40 00 movl 4194304, %eax + +# RUN: lld -flavor link /out:%t2.exe /subsystem:console /entry:_start \ +# RUN: /base:65536 /opt:noref -- %t.obj +# RUN: llvm-objdump -disassemble %t2.exe | FileCheck -check-prefix=BASE %s + +BASE: a1 00 00 01 00 movl 65536, %eax diff --git a/test/pecoff/importlib.test b/test/pecoff/importlib.test new file mode 100644 index 0000000000000..28e74f9dc78d1 --- /dev/null +++ b/test/pecoff/importlib.test @@ -0,0 +1,55 @@ +# REQUIRES: x86 + +# Verify that lld can handle .lib files. "main.obj" refers "var" and +# "fn" defined in "vars.lib". +# +# RUN: yaml2obj %p/Inputs/vars-main-x86.obj.yaml > %t.obj +# +# RUN: lld -flavor link /out:%t1.exe /subsystem:console /entry:main /opt:noref \ +# RUN: -- %t.obj %p/Inputs/vars.lib +# RUN: llvm-objdump -d %t1.exe | FileCheck -check-prefix=TEXT %s +# RUN: llvm-readobj -coff-imports %t1.exe | FileCheck -check-prefix=IMPORT %s +# +# RUN: lld -flavor link /out:%t2.exe /subsystem:console /entry:main /opt:noref \ +# RUN: /libpath:%p/Inputs -- %t.obj vars.lib +# RUN: llvm-objdump -d %t2.exe | FileCheck -check-prefix=TEXT %s +# RUN: llvm-readobj -coff-imports %t2.exe | FileCheck -check-prefix=IMPORT %s +# +# RUN: lld -flavor link /out:%t3.exe /subsystem:console /entry:main /opt:noref \ +# RUN: /libpath:%p/Inputs /defaultlib:vars.lib -- %t.obj +# RUN: llvm-objdump -d %t3.exe | FileCheck -check-prefix=TEXT %s +# RUN: llvm-readobj -coff-imports %t3.exe | FileCheck -check-prefix=IMPORT %s +# +# RUN: env LIB=%p/Inputs lld -flavor link /out:%t4.exe /subsystem:console \ +# RUN: /opt:noref /entry:main -- %t.obj vars.lib +# RUN: llvm-objdump -d %t4.exe | FileCheck -check-prefix=TEXT %s +# RUN: llvm-readobj -coff-imports %t4.exe | FileCheck -check-prefix=IMPORT %s +# +# RUN: env LINK="/out:%t5.exe /subsystem:console /entry:main /opt:noref \ +# RUN: -- %t.obj" lld -flavor link %p/Inputs/vars.lib +# RUN: llvm-objdump -d %t5.exe | FileCheck -check-prefix=TEXT %s +# RUN: llvm-readobj -coff-imports %t5.exe | FileCheck -check-prefix=IMPORT %s + +TEXT: Disassembly of section .text: +TEXT-NEXT: .text: +TEXT-NEXT: pushl %ebp +TEXT-NEXT: movl %esp, %ebp +TEXT-NEXT: pushl %esi +TEXT-NEXT: calll *{{[0-9]+}} +TEXT-NEXT: movl {{[0-9]+}}, %ecx +TEXT-NEXT: movl (%ecx), %esi +TEXT-NEXT: addl %eax, %esi +TEXT-NEXT: calll *{{[0-9]+}} +TEXT-NEXT: addl %esi, %eax +TEXT-NEXT: popl %esi +TEXT-NEXT: popl %ebp +TEXT-NEXT: ret + +IMPORT: Import { +IMPORT-NEXT: Name: vars.dll +IMPORT-NEXT: ImportLookupTableRVA: 0x4000 +IMPORT-NEXT: ImportAddressTableRVA: 0x2000 +IMPORT-NEXT: Symbol: _name_with_underscore (0) +IMPORT-NEXT: Symbol: fn (1) +IMPORT-NEXT: Symbol: (1) +IMPORT-NEXT: } diff --git a/test/pecoff/include.test b/test/pecoff/include.test new file mode 100644 index 0000000000000..bee1f48bf1a73 --- /dev/null +++ b/test/pecoff/include.test @@ -0,0 +1,8 @@ +# RUN: yaml2obj %p/Inputs/nop.obj.yaml > %t.obj +# +# RUN: not lld -flavor link /out:%t.exe /include:sym1 /include:sym2 \ +# RUN: /subsystem:console -- %t.obj 2> %t.log +# RUN: FileCheck %s < %t.log + +CHECK: Undefined symbol: <command line option /include>: sym1 +CHECK: Undefined symbol: <command line option /include>: sym2 diff --git a/test/pecoff/lib.test b/test/pecoff/lib.test new file mode 100644 index 0000000000000..f435c117bc468 --- /dev/null +++ b/test/pecoff/lib.test @@ -0,0 +1,15 @@ +# REQUIRES: x86 + +# Verify that lld can handle a library file. +# +# RUN: yaml2obj %p/Inputs/main.obj.yaml > %t.obj +# +# RUN: lld -flavor link /out:%t.exe /subsystem:console /entry:main /opt:noref \ +# RUN: -- %t.obj %p/Inputs/static.lib +# RUN: llvm-objdump -d %t.exe | FileCheck %s + +CHECK: Disassembly of section .text: +CHECK-NEXT: .text: +CHECK-NEXT: movl 4198400, %eax +CHECK-NEXT: addl 4198404, %eax +CHECK-NEXT: ret diff --git a/test/pecoff/libarg.test b/test/pecoff/libarg.test new file mode 100644 index 0000000000000..314f030c4fe0d --- /dev/null +++ b/test/pecoff/libarg.test @@ -0,0 +1,9 @@ +# REQUIRES: winlib +# +# If argv[1] == "/lib", link.exe morphs into lib.exe. +# +# RUN: lld -flavor link /lib >& %t.log +# RUN: FileCheck %s < %t.log + +CHECK-NOT: unrecognized option '/lib' +CHECK: usage: LIB diff --git a/test/pecoff/localyimported.test b/test/pecoff/localyimported.test new file mode 100644 index 0000000000000..52b32d7b80bd9 --- /dev/null +++ b/test/pecoff/localyimported.test @@ -0,0 +1,15 @@ +# RUN: yaml2obj %p/Inputs/hello.obj.yaml > %t.obj +# +# RUN: not lld -flavor link /out:%t.exe /include:__imp__nosuchsym %t.obj \ +# RUN: >& %t.log +# RUN: FileCheck -check-prefix=X86 %s < %t.log + +X86: Undefined symbol: __imp__nosuchsym: _nosuchsym + +# RUN: yaml2obj %p/Inputs/hello64.obj.yaml > %t2.obj +# +# RUN: not lld -flavor link /out:%t2.exe /include:__imp__nosuchsym %t2.obj \ +# RUN: /machine:x64 >& %t2.log +# RUN: FileCheck -check-prefix=X64 %s < %t2.log + +X64: Undefined symbol: __imp__nosuchsym: _nosuchsym diff --git a/test/pecoff/long-section-name.test b/test/pecoff/long-section-name.test new file mode 100644 index 0000000000000..e6721c28302f4 --- /dev/null +++ b/test/pecoff/long-section-name.test @@ -0,0 +1,7 @@ +# RUN: yaml2obj %p/Inputs/nop.obj.yaml > %t.obj +# +# RUN: lld -flavor link /out:%t.exe /subsystem:console /entry:start \ +# RUN: /merge:.text=.longsectionname -- %t.obj +# RUN: llvm-readobj -sections %t.exe | FileCheck %s + +CHECK: Name: .longsectionname (2F 34 00 00 00 00 00 00) diff --git a/test/pecoff/machinetype.test b/test/pecoff/machinetype.test new file mode 100644 index 0000000000000..5d387f3b03934 --- /dev/null +++ b/test/pecoff/machinetype.test @@ -0,0 +1,13 @@ +# RUN: yaml2obj %p/Inputs/machine-type-unknown.obj.yaml > %t1.obj +# RUN: yaml2obj %p/Inputs/hello.obj.yaml > %t2.obj +# RUN: yaml2obj %p/Inputs/hello64.obj.yaml > %t3.obj + +# RUN: lld -flavor link /out:%t.exe /subsystem:console /force -- %t1.obj %t2.obj +# RUN: llvm-readobj %t.exe | FileCheck -check-prefix=X86 %s + +X86: Arch: i386 + +# RUN: lld -flavor link /out:%t.exe /subsystem:console /force -- %t1.obj %t3.obj +# RUN: llvm-readobj %t.exe | FileCheck -check-prefix=X64 %s + +X64: Arch: x86_64 diff --git a/test/pecoff/manifest.test b/test/pecoff/manifest.test new file mode 100644 index 0000000000000..33229a45516b8 --- /dev/null +++ b/test/pecoff/manifest.test @@ -0,0 +1,63 @@ +# RUN: yaml2obj %p/Inputs/nop.obj.yaml > %t.obj + +# RUN: lld -flavor link /out:%t1.exe /subsystem:console /force \ +# RUN: -- %t.obj +# RUN: FileCheck -check-prefix=MANIFEST %s < %t1.exe.manifest + +MANIFEST: <?xml version="1.0" standalone="yes"?> +MANIFEST: <assembly xmlns="urn:schemas-microsoft-com:asm.v1" +MANIFEST: manifestVersion="1.0"> +MANIFEST: <trustInfo> +MANIFEST: <security> +MANIFEST: <requestedPrivileges> +MANIFEST: <requestedExecutionLevel level='asInvoker' uiAccess='false'/> +MANIFEST: </requestedPrivileges> +MANIFEST: </security> +MANIFEST: </trustInfo> +MANIFEST: </assembly> + +# RUN: lld -flavor link /out:%t2.exe /subsystem:console /force \ +# RUN: /manifestuac:"level='requireAdministrator' uiAccess='true'" -- %t.obj +# RUN: FileCheck -check-prefix=UAC %s < %t2.exe.manifest + +UAC: <?xml version="1.0" standalone="yes"?> +UAC: <assembly xmlns="urn:schemas-microsoft-com:asm.v1" +UAC: manifestVersion="1.0"> +UAC: <trustInfo> +UAC: <security> +UAC: <requestedPrivileges> +UAC: <requestedExecutionLevel level='requireAdministrator' uiAccess='true'/> +UAC: </requestedPrivileges> +UAC: </security> +UAC: </trustInfo> +UAC: </assembly> + +# RUN: lld -flavor link /out:%t3.exe /subsystem:console /force \ +# RUN: /manifestdependency:"foo='bar'" -- %t.obj +# RUN: FileCheck -check-prefix=DEPENDENCY %s < %t3.exe.manifest + +DEPENDENCY: <?xml version="1.0" standalone="yes"?> +DEPENDENCY: <assembly xmlns="urn:schemas-microsoft-com:asm.v1" +DEPENDENCY: manifestVersion="1.0"> +DEPENDENCY: <trustInfo> +DEPENDENCY: <security> +DEPENDENCY: <requestedPrivileges> +DEPENDENCY: <requestedExecutionLevel level='asInvoker' uiAccess='false'/> +DEPENDENCY: </requestedPrivileges> +DEPENDENCY: </security> +DEPENDENCY: </trustInfo> +DEPENDENCY: <dependency> +DEPENDENCY: <dependentAssembly> +DEPENDENCY: <assemblyIdentity foo='bar' /> +DEPENDENCY: </dependentAssembly> +DEPENDENCY: </dependency> +DEPENDENCY: </assembly> + +# RUN: lld -flavor link /out:%t4.exe /subsystem:console /force \ +# RUN: /manifestuac:no -- %t.obj +# RUN: FileCheck -check-prefix=NOUAC %s < %t4.exe.manifest + +NOUAC: <?xml version="1.0" standalone="yes"?> +NOUAC: <assembly xmlns="urn:schemas-microsoft-com:asm.v1" +NOUAC: manifestVersion="1.0"> +NOUAC: </assembly> diff --git a/test/pecoff/merge-largest.test b/test/pecoff/merge-largest.test new file mode 100644 index 0000000000000..c3ee96ca9c53d --- /dev/null +++ b/test/pecoff/merge-largest.test @@ -0,0 +1,24 @@ +# RUN: yaml2obj %p/Inputs/merge-largest1.obj.yaml > %t1.obj +# RUN: yaml2obj %p/Inputs/merge-largest2.obj.yaml > %t2.obj +# +# RUN: lld -flavor link /out:%t.exe /subsystem:console /opt:noref /force \ +# RUN: -- %t1.obj %t2.obj 2>&1 > %t.log +# +# FileCheck complains if the input files is empty, so add a dummy line. +# RUN: echo foo >> %t.log +# RUN: FileCheck -check-prefix=STDERR %s < %t.log +# +# RUN: llvm-readobj -sections %t.exe | FileCheck -check-prefix=READOBJ %s + +STDERR-NOT: duplicate symbol error + +READOBJ: Format: COFF-i386 +READOBJ-NEXT: Arch: i386 +READOBJ-NEXT: AddressSize: 32bit +READOBJ-NEXT: Sections [ +READOBJ-NEXT: Section { +READOBJ-NEXT: Number: 1 +READOBJ-NEXT: Name: .text (2E 74 65 78 74 00 00 00) +READOBJ-NEXT: VirtualSize: 0x8 +READOBJ-NEXT: VirtualAddress: 0x1000 +READOBJ-NEXT: RawDataSize: 512 diff --git a/test/pecoff/merge-same-size.test b/test/pecoff/merge-same-size.test new file mode 100644 index 0000000000000..c2918a2bc1a4d --- /dev/null +++ b/test/pecoff/merge-same-size.test @@ -0,0 +1,32 @@ +# RUN: yaml2obj %p/Inputs/merge-same-size1.obj.yaml > %t1.obj +# RUN: yaml2obj %p/Inputs/merge-same-size2.obj.yaml > %t2.obj +# RUN: yaml2obj %p/Inputs/merge-same-size3.obj.yaml > %t3.obj +# +# RUN: lld -flavor link /out:%t.exe /subsystem:console /opt:noref /force \ +# RUN: -- %t1.obj %t2.obj > %t1.log 2>&1 +# +# FileCheck complains if the input files is empty, so add a dummy line. +# RUN: echo foo >> %t1.log +# RUN: FileCheck -check-prefix=SAMESIZE %s < %t1.log +# +# RUN: not lld -flavor link /out:%t.exe /subsystem:console /opt:noref /force \ +# RUN: -- %t1.obj %t3.obj > %t2.log 2>&1 +# RUN: FileCheck -check-prefix=DIFFERENT %s < %t2.log +# +# RUN: llvm-readobj -sections %t.exe | FileCheck -check-prefix=READOBJ %s + +SAMESIZE-NOT: duplicate symbol error + +DIFFERENT: Size mismatch +DIFFERENT: duplicate symbol error + +READOBJ: Format: COFF-i386 +READOBJ-NEXT: Arch: i386 +READOBJ-NEXT: AddressSize: 32bit +READOBJ-NEXT: Sections [ +READOBJ-NEXT: Section { +READOBJ-NEXT: Number: 1 +READOBJ-NEXT: Name: .text (2E 74 65 78 74 00 00 00) +READOBJ-NEXT: VirtualSize: 0x7 +READOBJ-NEXT: VirtualAddress: 0x1000 +READOBJ-NEXT: RawDataSize: 512 diff --git a/test/pecoff/multi.test b/test/pecoff/multi.test new file mode 100644 index 0000000000000..e0bfdba6dda41 --- /dev/null +++ b/test/pecoff/multi.test @@ -0,0 +1,17 @@ +# REQUIRES: x86 + +# Verify that lld can handle multiple input files. +# +# RUN: yaml2obj %p/Inputs/main.obj.yaml > %t1.obj +# RUN: yaml2obj %p/Inputs/static-data1.obj.yaml > %t2.obj +# RUN: yaml2obj %p/Inputs/static-data2.obj.yaml > %t3.obj +# +# RUN: lld -flavor link /out:%t.exe /subsystem:console /entry:main /opt:noref \ +# RUN: -- %t1.obj %t2.obj %t3.obj +# RUN: llvm-objdump -d %t.exe | FileCheck %s + +CHECK: Disassembly of section .text: +CHECK: .text: +CHECK: movl {{[0-9]+}}, %eax +CHECK: addl {{[0-9]+}}, %eax +CHECK: ret diff --git a/test/pecoff/noentry.test b/test/pecoff/noentry.test new file mode 100644 index 0000000000000..55b326dd84cb5 --- /dev/null +++ b/test/pecoff/noentry.test @@ -0,0 +1,10 @@ +# RUN: yaml2obj %p/Inputs/nop.obj.yaml > %t.obj +# RUN: lld -flavor link /out:%t.exe /noentry /dll -- %t.obj +# RUN: llvm-readobj -file-headers %t.exe | FileCheck %s + +CHECK: AddressOfEntryPoint: 0x0 + +# RUN: not lld -flavor link /out:%t.exe /noentry -- %t.obj >& %t.log +# RUN: FileCheck --check-prefix=ERROR %s < %t.log + +ERROR: /noentry must be specified with /dll diff --git a/test/pecoff/nonstandard-sections.test b/test/pecoff/nonstandard-sections.test new file mode 100644 index 0000000000000..2ca181629230b --- /dev/null +++ b/test/pecoff/nonstandard-sections.test @@ -0,0 +1,75 @@ +# RUN: yaml2obj %p/Inputs/nonstandard-sections.obj.yaml > %t.obj +# RUN: lld -flavor link /out:%t.exe /subsystem:console /force -- %t.obj +# RUN: llvm-readobj -sections %t.exe | FileCheck %s + +CHECK: Arch: i386 +CHECK-NEXT: AddressSize: 32bit +CHECK-NEXT: Sections [ +CHECK-NEXT: Section { +CHECK-NEXT: Number: 1 +CHECK-NEXT: Name: .bar (2E 62 61 72 00 00 00 00) +CHECK-NEXT: VirtualSize: 0x4 +CHECK-NEXT: VirtualAddress: 0x1000 +CHECK-NEXT: RawDataSize: 512 +CHECK-NEXT: PointerToRawData: 0x400 +CHECK-NEXT: PointerToRelocations: 0x0 +CHECK-NEXT: PointerToLineNumbers: 0x0 +CHECK-NEXT: RelocationCount: 0 +CHECK-NEXT: LineNumberCount: 0 +CHECK-NEXT: Characteristics [ (0x40000040) +CHECK-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA (0x40) +CHECK-NEXT: IMAGE_SCN_MEM_READ (0x40000000) +CHECK-NEXT: ] +CHECK-NEXT: } +CHECK-NEXT: Section { +CHECK-NEXT: Number: 2 +CHECK-NEXT: Name: .data (2E 64 61 74 61 00 00 00) +CHECK-NEXT: VirtualSize: 0x4 +CHECK-NEXT: VirtualAddress: 0x2000 +CHECK-NEXT: RawDataSize: 512 +CHECK-NEXT: PointerToRawData: 0x600 +CHECK-NEXT: PointerToRelocations: 0x0 +CHECK-NEXT: PointerToLineNumbers: 0x0 +CHECK-NEXT: RelocationCount: 0 +CHECK-NEXT: LineNumberCount: 0 +CHECK-NEXT: Characteristics [ (0xC0000040) +CHECK-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA (0x40) +CHECK-NEXT: IMAGE_SCN_MEM_READ (0x40000000) +CHECK-NEXT: IMAGE_SCN_MEM_WRITE (0x80000000) +CHECK-NEXT: ] +CHECK-NEXT: } +CHECK-NEXT: Section { +CHECK-NEXT: Number: 3 +CHECK-NEXT: Name: .foo (2E 66 6F 6F 00 00 00 00) +CHECK-NEXT: VirtualSize: 0x4 +CHECK-NEXT: VirtualAddress: 0x3000 +CHECK-NEXT: RawDataSize: 512 +CHECK-NEXT: PointerToRawData: 0x800 +CHECK-NEXT: PointerToRelocations: 0x0 +CHECK-NEXT: PointerToLineNumbers: 0x0 +CHECK-NEXT: RelocationCount: 0 +CHECK-NEXT: LineNumberCount: 0 +CHECK-NEXT: Characteristics [ (0xC0000040) +CHECK-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA (0x40) +CHECK-NEXT: IMAGE_SCN_MEM_READ (0x40000000) +CHECK-NEXT: IMAGE_SCN_MEM_WRITE (0x80000000) +CHECK-NEXT: ] +CHECK-NEXT: } +CHECK-NEXT: Section { +CHECK-NEXT: Number: 4 +CHECK-NEXT: Name: .text (2E 74 65 78 74 00 00 00) +CHECK-NEXT: VirtualSize: 0x4 +CHECK-NEXT: VirtualAddress: 0x4000 +CHECK-NEXT: RawDataSize: 512 +CHECK-NEXT: PointerToRawData: 0xA00 +CHECK-NEXT: PointerToRelocations: 0x0 +CHECK-NEXT: PointerToLineNumbers: 0x0 +CHECK-NEXT: RelocationCount: 0 +CHECK-NEXT: LineNumberCount: 0 +CHECK-NEXT: Characteristics [ (0x60000020) +CHECK-NEXT: IMAGE_SCN_CNT_CODE (0x20) +CHECK-NEXT: IMAGE_SCN_MEM_EXECUTE (0x20000000) +CHECK-NEXT: IMAGE_SCN_MEM_READ (0x40000000) +CHECK-NEXT: ] +CHECK-NEXT: } +CHECK-NEXT: ] diff --git a/test/pecoff/options.test b/test/pecoff/options.test new file mode 100644 index 0000000000000..3aca2c9fcc916 --- /dev/null +++ b/test/pecoff/options.test @@ -0,0 +1,40 @@ +# Tests for miscellaneous command line options. + +# RUN: yaml2obj %p/Inputs/nop.obj.yaml > %t-x86.obj +# RUN: yaml2obj %p/Inputs/nop64.obj.yaml > %t-x64.obj + +# RUN: lld -flavor link /align:8192 /out:%t.exe /entry:start \ +# RUN: /subsystem:console -- %t-x86.obj +# RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=ALIGN %s +ALIGN: SectionAlignment: 8192 + +# RUN: lld -flavor link /allowbind:no /out:%t.exe /entry:start \ +# RUN: /subsystem:console -- %t-x86.obj +# RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=NOBIND %s +NOBIND: IMAGE_DLL_CHARACTERISTICS_NO_BIND + +# RUN: lld -flavor link /allowisolation:no /out:%t.exe /entry:start \ +# RUN: /subsystem:console -- %t-x86.obj +# RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=NOISO %s +NOISO: IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION + +# RUN: lld -flavor link /swaprun:cd /out:%t.exe /entry:start \ +# RUN: /subsystem:console -- %t-x86.obj +# RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=RUNCD %s +RUNCD: IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP + +# RUN: lld -flavor link /swaprun:net /out:%t.exe /entry:start \ +# RUN: /subsystem:console -- %t-x86.obj +# RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=RUNNET %s +RUNNET: IMAGE_FILE_NET_RUN_FROM_SWAP + +# RUN: lld -flavor link /machine:x64 /force /highentropyva /out:%t.exe \ +# RUN: /entry:start /subsystem:console -- %t-x64.obj +# RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=ENT %s +ENT: IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA + +# RUN: lld -flavor link /machine:x64 /force /highentropyva:no /out:%t.exe \ +# RUN: /entry:start /subsystem:console -- %t-x64.obj +# RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=NOENT %s +NOENT-NOT: IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA + diff --git a/test/pecoff/pe32plus.test b/test/pecoff/pe32plus.test new file mode 100644 index 0000000000000..1c64e184cbaf7 --- /dev/null +++ b/test/pecoff/pe32plus.test @@ -0,0 +1,87 @@ +# RUN: yaml2obj %p/Inputs/nop64.obj.yaml > %t.obj + +# RUN: lld -flavor link /out:%t.exe /subsystem:console /entry:start \ +# RUN: /machine:x64 -- %t.obj %p/Inputs/vars.lib +# RUN: llvm-readobj -file-headers %t.exe | FileCheck %s + +CHECK: Format: COFF-x86-64 +CHECK-NEXT: Arch: x86_64 +CHECK-NEXT: AddressSize: 64bit +CHECK-NEXT: ImageFileHeader { +CHECK-NEXT: Machine: IMAGE_FILE_MACHINE_AMD64 (0x8664) +CHECK-NEXT: SectionCount: 5 +CHECK-NEXT: TimeDateStamp: +CHECK-NEXT: PointerToSymbolTable: 0x0 +CHECK-NEXT: SymbolCount: 0 +CHECK-NEXT: OptionalHeaderSize: 240 +CHECK-NEXT: Characteristics [ (0x22) +CHECK-NEXT: IMAGE_FILE_EXECUTABLE_IMAGE (0x2) +CHECK-NEXT: IMAGE_FILE_LARGE_ADDRESS_AWARE (0x20) +CHECK-NEXT: ] +CHECK-NEXT: } +CHECK-NEXT: ImageOptionalHeader { +CHECK-NEXT: MajorLinkerVersion: 0 +CHECK-NEXT: MinorLinkerVersion: 0 +CHECK-NEXT: SizeOfCode: 512 +CHECK-NEXT: SizeOfInitializedData: 2048 +CHECK-NEXT: SizeOfUninitializedData: 0 +CHECK-NEXT: AddressOfEntryPoint: 0x5000 +CHECK-NEXT: BaseOfCode: 0x5000 +CHECK-NEXT: ImageBase: 0x140000000 +CHECK-NEXT: SectionAlignment: 4096 +CHECK-NEXT: FileAlignment: 512 +CHECK-NEXT: MajorOperatingSystemVersion: 6 +CHECK-NEXT: MinorOperatingSystemVersion: 0 +CHECK-NEXT: MajorImageVersion: 0 +CHECK-NEXT: MinorImageVersion: 0 +CHECK-NEXT: MajorSubsystemVersion: 6 +CHECK-NEXT: MinorSubsystemVersion: 0 +CHECK-NEXT: SizeOfImage: 24576 +CHECK-NEXT: SizeOfHeaders: 1024 +CHECK-NEXT: Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI (0x3) +CHECK-NEXT: Characteristics [ (0x8160) +CHECK-NEXT: IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE (0x40) +CHECK-NEXT: IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA (0x20) +CHECK-NEXT: IMAGE_DLL_CHARACTERISTICS_NX_COMPAT (0x100) +CHECK-NEXT: IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE (0x8000) +CHECK-NEXT: ] +CHECK-NEXT: SizeOfStackReserve: 1048576 +CHECK-NEXT: SizeOfStackCommit: 4096 +CHECK-NEXT: SizeOfHeapReserve: 1048576 +CHECK-NEXT: SizeOfHeapCommit: 4096 +CHECK-NEXT: NumberOfRvaAndSize: 16 +CHECK-NEXT: DataDirectory { +CHECK-NEXT: ExportTableRVA: 0x0 +CHECK-NEXT: ExportTableSize: 0x0 +CHECK-NEXT: ImportTableRVA: 0x3000 +CHECK-NEXT: ImportTableSize: 0x28 +CHECK-NEXT: ResourceTableRVA: 0x0 +CHECK-NEXT: ResourceTableSize: 0x0 +CHECK-NEXT: ExceptionTableRVA: 0x0 +CHECK-NEXT: ExceptionTableSize: 0x0 +CHECK-NEXT: CertificateTableRVA: 0x0 +CHECK-NEXT: CertificateTableSize: 0x0 +CHECK-NEXT: BaseRelocationTableRVA: 0x0 +CHECK-NEXT: BaseRelocationTableSize: 0x0 +CHECK-NEXT: DebugRVA: 0x0 +CHECK-NEXT: DebugSize: 0x0 +CHECK-NEXT: ArchitectureRVA: 0x0 +CHECK-NEXT: ArchitectureSize: 0x0 +CHECK-NEXT: GlobalPtrRVA: 0x0 +CHECK-NEXT: GlobalPtrSize: 0x0 +CHECK-NEXT: TLSTableRVA: 0x0 +CHECK-NEXT: TLSTableSize: 0x0 +CHECK-NEXT: LoadConfigTableRVA: 0x0 +CHECK-NEXT: LoadConfigTableSize: 0x0 +CHECK-NEXT: BoundImportRVA: 0x0 +CHECK-NEXT: BoundImportSize: 0x0 +CHECK-NEXT: IATRVA: 0x2000 +CHECK-NEXT: IATSize: 0x10 +CHECK-NEXT: DelayImportDescriptorRVA: 0x0 +CHECK-NEXT: DelayImportDescriptorSize: 0x0 +CHECK-NEXT: CLRRuntimeHeaderRVA: 0x0 +CHECK-NEXT: CLRRuntimeHeaderSize: 0x0 +CHECK-NEXT: ReservedRVA: 0x0 +CHECK-NEXT: ReservedSize: 0x0 +CHECK-NEXT: } +CHECK-NEXT: } diff --git a/test/pecoff/reloc.test b/test/pecoff/reloc.test new file mode 100644 index 0000000000000..5a969e9beaa0b --- /dev/null +++ b/test/pecoff/reloc.test @@ -0,0 +1,16 @@ +# REQUIRES: x86 + +# RUN: yaml2obj %p/Inputs/reloc.obj.yaml > %t1.obj +# RUN: yaml2obj %p/Inputs/abs.obj.yaml > %t2.obj +# +# RUN: lld -flavor link /out:%t.exe /subsystem:console /force /opt:noref \ +# RUN: -- %t1.obj %t2.obj +# RUN: llvm-objdump -d %t.exe | FileCheck %s + +CHECK: .text: +CHECK: 3000: 68 02 00 00 00 +CHECK: 3005: 68 05 00 00 00 +CHECK: 300a: 68 00 10 40 00 +CHECK: 300f: 68 00 10 40 00 +CHECK: 3014: 68 00 20 40 00 +CHECK: 3019: 68 ef be ad de diff --git a/test/pecoff/reloc64.test b/test/pecoff/reloc64.test new file mode 100644 index 0000000000000..fc38bff032755 --- /dev/null +++ b/test/pecoff/reloc64.test @@ -0,0 +1,20 @@ +# REQUIRES: x86 + +# RUN: yaml2obj %p/Inputs/reloc64.obj.yaml > %t.obj + +# RUN: lld -flavor link /out:%t.exe /subsystem:console /machine:x64 \ +# RUN: /entry:entry -- %t.obj +# RUN: llvm-objdump -d %t.exe | FileCheck %s + +CHECK: Disassembly of section .text: +CHECK-NEXT: .text: +CHECK-NEXT: 1000: 48 b8 28 10 00 40 01 00 00 ff +CHECK-NEXT: 100a: e8 19 00 00 ff +CHECK-NEXT: 100f: e8 13 00 00 ff +CHECK-NEXT: 1014: e8 0d 00 00 ff +CHECK-NEXT: 1019: e8 07 00 00 ff +CHECK-NEXT: 101e: e8 01 00 00 ff +CHECK-NEXT: 1023: e8 fb ff ff fe +CHECK-NEXT: 1028: e8 01 00 00 ff +CHECK-NEXT: 102d: e8 28 00 00 ff +CHECK-NEXT: 1032: c3 diff --git a/test/pecoff/resource.test b/test/pecoff/resource.test new file mode 100644 index 0000000000000..8cdc9a5bf83f1 --- /dev/null +++ b/test/pecoff/resource.test @@ -0,0 +1,16 @@ +# REQUIRES: winres + +# RUN: yaml2obj %p/Inputs/nop.obj.yaml > %t.obj + +# RUN: lld -flavor link /out:%t.exe /subsystem:console /entry:start /opt:noref \ +# RUN: -- %t.obj %p/Inputs/resource.res + +# Check if the binary contains UTF-16LE string "Hello" copied from resource.res. +# RUN: cat %t.exe | grep 'H.e.l.l.o' + +# RUN: lld -flavor link /out:%t.exe /subsystem:console /entry:start /opt:noref \ +# RUN: /manifest:embed -- %t.obj %p/Inputs/resource.res +# RUN: llvm-readobj -file-headers %t.exe | FileCheck %s + +CHECK: ResourceTableRVA: 0x1000 +CHECK: ResourceTableSize: 0x208 diff --git a/test/pecoff/responsefile.test b/test/pecoff/responsefile.test new file mode 100644 index 0000000000000..6a5da298e730b --- /dev/null +++ b/test/pecoff/responsefile.test @@ -0,0 +1,7 @@ +# RUN: yaml2obj %p/Inputs/hello.obj.yaml > %t.obj +# RUN: not lld -flavor link /verbose @%p/Inputs/responsefile.txt >& %t.log +# RUN: FileCheck %s < %t.log + +CHECK: warning: ignoring unknown argument: -foo +CHECK: warning: ignoring unknown argument: -bar\baz +Command line: link /verbose -foo -bar\baz diff --git a/test/pecoff/safeseh.test b/test/pecoff/safeseh.test new file mode 100644 index 0000000000000..78b1b0b53ac14 --- /dev/null +++ b/test/pecoff/safeseh.test @@ -0,0 +1,9 @@ +# "hello.obj" does not have the symbol "@feat.00", so it's not +# compatible with SEH. + +# RUN: yaml2obj %p/Inputs/hello.obj.yaml > %t1.obj +# RUN: not lld -flavor link /safeseh /out:%t1.exe /subsystem:console \ +# RUN: -- %t1.obj 2> %t1.err +# RUN: FileCheck -check-prefix=INCOMPAT %s < %t1.err + +INCOMPAT: /SAFESEH is specified, but {{.*}} is not compatible with SEH. diff --git a/test/pecoff/secrel.test b/test/pecoff/secrel.test new file mode 100644 index 0000000000000..05014c47af48c --- /dev/null +++ b/test/pecoff/secrel.test @@ -0,0 +1,16 @@ +# RUN: yaml2obj %p/Inputs/secrel1.obj.yaml > %t1.obj +# RUN: yaml2obj %p/Inputs/secrel2.obj.yaml > %t2.obj +# RUN: yaml2obj %p/Inputs/secrel2.obj.yaml > %t3.obj +# +# RUN: lld -flavor link /out:%t.exe /subsystem:console /entry:main \ +# RUN: -- %t1.obj %t2.obj %t3.obj +# RUN: llvm-objdump -s %t.exe | FileCheck %s + +CHECK: Contents of section .data: +CHECK: 1000 00000000 00000000 00000000 00000000 +CHECK: 1010 10000000 00000000 00000000 00000000 +CHECK: 1020 20000000 00000000 00000000 00000000 +CHECK: Contents of section .data2: +CHECK: 2000 00000000 00000000 00000000 00000000 +CHECK: 2010 10000000 00000000 00000000 00000000 +CHECK: 2020 20000000 00000000 00000000 00000000 diff --git a/test/pecoff/section-attribute.test b/test/pecoff/section-attribute.test new file mode 100644 index 0000000000000..a5e71625df991 --- /dev/null +++ b/test/pecoff/section-attribute.test @@ -0,0 +1,45 @@ +# RUN: yaml2obj %p/Inputs/nonstandard-sections.obj.yaml > %t.obj +# RUN: lld -flavor link /out:%t.exe /subsystem:console /force \ +# RUN: /section:.foo,d /section:.bar,rw /section:.text,rwe -- %t.obj +# RUN: llvm-readobj -sections %t.exe | FileCheck %s + +CHECK: Sections [ +CHECK: Section { +CHECK: Number: 1 +CHECK: Name: .bar (2E 62 61 72 00 00 00 00) +CHECK: Characteristics [ (0xC0000040) +CHECK: IMAGE_SCN_CNT_INITIALIZED_DATA (0x40) +CHECK: IMAGE_SCN_MEM_READ (0x40000000) +CHECK: IMAGE_SCN_MEM_WRITE (0x80000000) +CHECK: ] +CHECK: } +CHECK: Section { +CHECK: Number: 2 +CHECK: Name: .data (2E 64 61 74 61 00 00 00) +CHECK: Characteristics [ (0xC0000040) +CHECK: IMAGE_SCN_CNT_INITIALIZED_DATA (0x40) +CHECK: IMAGE_SCN_MEM_READ (0x40000000) +CHECK: IMAGE_SCN_MEM_WRITE (0x80000000) +CHECK: ] +CHECK: } +CHECK: Section { +CHECK: Number: 3 +CHECK: Name: .foo (2E 66 6F 6F 00 00 00 00) +CHECK: Characteristics [ (0xC2000040) +CHECK: IMAGE_SCN_CNT_INITIALIZED_DATA (0x40) +CHECK: IMAGE_SCN_MEM_DISCARDABLE (0x2000000) +CHECK: IMAGE_SCN_MEM_READ (0x40000000) +CHECK: IMAGE_SCN_MEM_WRITE (0x80000000) +CHECK: ] +CHECK: } +CHECK: Section { +CHECK: Number: 4 +CHECK: Name: .text (2E 74 65 78 74 00 00 00) +CHECK: Characteristics [ (0xE0000020) +CHECK: IMAGE_SCN_CNT_CODE (0x20) +CHECK: IMAGE_SCN_MEM_EXECUTE (0x20000000) +CHECK: IMAGE_SCN_MEM_READ (0x40000000) +CHECK: IMAGE_SCN_MEM_WRITE (0x80000000) +CHECK: ] +CHECK: } +CHECK: ] diff --git a/test/pecoff/section-renaming.test b/test/pecoff/section-renaming.test new file mode 100644 index 0000000000000..d4fc154693af6 --- /dev/null +++ b/test/pecoff/section-renaming.test @@ -0,0 +1,61 @@ +# RUN: yaml2obj %p/Inputs/nonstandard-sections.obj.yaml > %t.obj +# RUN: lld -flavor link /out:%t.exe /subsystem:console /force \ +# RUN: /merge:.foo=.hoge /merge:.bar=.text -- %t.obj +# RUN: llvm-readobj -sections %t.exe | FileCheck %s + +CHECK: Format: COFF-i386 +CHECK-NEXT: Arch: i386 +CHECK-NEXT: AddressSize: 32bit +CHECK-NEXT: Sections [ +CHECK-NEXT: Section { +CHECK-NEXT: Number: 1 +CHECK-NEXT: Name: .data (2E 64 61 74 61 00 00 00) +CHECK-NEXT: VirtualSize: 0x4 +CHECK-NEXT: VirtualAddress: 0x1000 +CHECK-NEXT: RawDataSize: 512 +CHECK-NEXT: PointerToRawData: 0x200 +CHECK-NEXT: PointerToRelocations: 0x0 +CHECK-NEXT: PointerToLineNumbers: 0x0 +CHECK-NEXT: RelocationCount: 0 +CHECK-NEXT: LineNumberCount: 0 +CHECK-NEXT: Characteristics [ (0xC0000040) +CHECK-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA (0x40) +CHECK-NEXT: IMAGE_SCN_MEM_READ (0x40000000) +CHECK-NEXT: IMAGE_SCN_MEM_WRITE (0x80000000) +CHECK-NEXT: ] +CHECK-NEXT: } +CHECK-NEXT: Section { +CHECK-NEXT: Number: 2 +CHECK-NEXT: Name: .hoge (2E 68 6F 67 65 00 00 00) +CHECK-NEXT: VirtualSize: 0x4 +CHECK-NEXT: VirtualAddress: 0x2000 +CHECK-NEXT: RawDataSize: 512 +CHECK-NEXT: PointerToRawData: 0x400 +CHECK-NEXT: PointerToRelocations: 0x0 +CHECK-NEXT: PointerToLineNumbers: 0x0 +CHECK-NEXT: RelocationCount: 0 +CHECK-NEXT: LineNumberCount: 0 +CHECK-NEXT: Characteristics [ (0xC0000040) +CHECK-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA (0x40) +CHECK-NEXT: IMAGE_SCN_MEM_READ (0x40000000) +CHECK-NEXT: IMAGE_SCN_MEM_WRITE (0x80000000) +CHECK-NEXT: ] +CHECK-NEXT: } +CHECK-NEXT: Section { +CHECK-NEXT: Number: 3 +CHECK-NEXT: Name: .text (2E 74 65 78 74 00 00 00) +CHECK-NEXT: VirtualSize: 0x8 +CHECK-NEXT: VirtualAddress: 0x3000 +CHECK-NEXT: RawDataSize: 512 +CHECK-NEXT: PointerToRawData: 0x600 +CHECK-NEXT: PointerToRelocations: 0x0 +CHECK-NEXT: PointerToLineNumbers: 0x0 +CHECK-NEXT: RelocationCount: 0 +CHECK-NEXT: LineNumberCount: 0 +CHECK-NEXT: Characteristics [ (0x60000020) +CHECK-NEXT: IMAGE_SCN_CNT_CODE (0x20) +CHECK-NEXT: IMAGE_SCN_MEM_EXECUTE (0x20000000) +CHECK-NEXT: IMAGE_SCN_MEM_READ (0x40000000) +CHECK-NEXT: ] +CHECK-NEXT: } +CHECK-NEXT: ] diff --git a/test/pecoff/seh.test b/test/pecoff/seh.test new file mode 100644 index 0000000000000..c563dc3f31e02 --- /dev/null +++ b/test/pecoff/seh.test @@ -0,0 +1,31 @@ +# RUN: yaml2obj %p/Inputs/seh.obj.yaml > %t.obj +# +# RUN: lld -flavor link /out:%t.exe /subsystem:console /force /nodefaultlib \ +# RUN: -- %t.obj +# RUN: llvm-objdump -private-headers %t.exe | FileCheck %s + +CHECK: Load configuration: +CHECK: Timestamp: 0 +CHECK: Major Version: 0 +CHECK: Minor Version: 0 +CHECK: GlobalFlags Clear: 0 +CHECK: GlobalFlags Set: 0 +CHECK: Critical Section Default Timeout: 0 +CHECK: Decommit Free Block Threshold: 0 +CHECK: Decommit Total Free Threshold: 0 +CHECK: Lock Prefix Table: 0 +CHECK: Maximum Allocation Size: 0 +CHECK: Virtual Memory Threshold: 0 +CHECK: Process Affinity Mask: 0 +CHECK: Process Heap Flags: 0 +CHECK: CSD Version: 0 +CHECK: Security Cookie: 0 +CHECK: SEH Table: 4206592 +CHECK: SEH Count: 2 +CHECK: SEH Table: 0x{{[0-9a-f]+}} 0x{{[0-9a-f]+}} + +# RUN: lld -flavor link /out:%t.exe /subsystem:console /force /nodefaultlib \ +# RUN: /safeseh:no -- %t.obj +# RUN: llvm-objdump -private-headers %t.exe | FileCheck -check-prefix=NOSEH %s + +NOSEH-NOT: SEH Table: diff --git a/test/pecoff/seh64.test b/test/pecoff/seh64.test new file mode 100644 index 0000000000000..664ec29e52583 --- /dev/null +++ b/test/pecoff/seh64.test @@ -0,0 +1,57 @@ +# RUN: yaml2obj %p/Inputs/unwind.obj.yaml > %t.obj +# +# RUN: lld -flavor link /machine:x64 /out:%t.exe /subsystem:console /force \ +# RUN: /nodefaultlib -- %t.obj +# RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=HEADER %s +# RUN: llvm-objdump -unwind-info %t.exe | FileCheck -check-prefix=UNWIND %s + +HEADER: ExceptionTableRVA: 0x1000 + +UNWIND: Function Table: +UNWIND: Start Address: 0x2000 +UNWIND: End Address: 0x201b +UNWIND: Unwind Info Address: 0x3000 +UNWIND: Version: 1 +UNWIND: Flags: 1 UNW_ExceptionHandler +UNWIND: Size of prolog: 18 +UNWIND: Number of Codes: 8 +UNWIND: Frame register: RBX +UNWIND: Frame offset: 0 +UNWIND: Unwind Codes: +UNWIND: 0x12: UOP_SetFPReg +UNWIND: 0x0f: UOP_PushNonVol RBX +UNWIND: 0x0e: UOP_SaveXMM128 XMM8 [0x0000] +UNWIND: 0x09: UOP_SaveNonVol RSI [0x0010] +UNWIND: 0x04: UOP_AllocSmall 24 +UNWIND: 0x00: UOP_PushMachFrame w/o error code +UNWIND: Function Table: +UNWIND: Start Address: 0x2012 +UNWIND: End Address: 0x2012 +UNWIND: Unwind Info Address: 0x301c +UNWIND: Version: 1 +UNWIND: Flags: 4 UNW_ChainInfo +UNWIND: Size of prolog: 0 +UNWIND: Number of Codes: 0 +UNWIND: No frame pointer used +UNWIND: Function Table: +UNWIND: Start Address: 0x201b +UNWIND: End Address: 0x201c +UNWIND: Unwind Info Address: 0x302c +UNWIND: Version: 1 +UNWIND: Flags: 0 +UNWIND: Size of prolog: 0 +UNWIND: Number of Codes: 0 +UNWIND: No frame pointer used +UNWIND: Function Table: +UNWIND: Start Address: 0x201c +UNWIND: End Address: 0x2039 +UNWIND: Unwind Info Address: 0x3034 +UNWIND: Version: 1 +UNWIND: Flags: 0 +UNWIND: Size of prolog: 14 +UNWIND: Number of Codes: 6 +UNWIND: No frame pointer used +UNWIND: Unwind Codes: +UNWIND: 0x0e: UOP_AllocLarge 8454128 +UNWIND: 0x07: UOP_AllocLarge 8190 +UNWIND: 0x00: UOP_PushMachFrame w/o error code diff --git a/test/pecoff/subsystem.test b/test/pecoff/subsystem.test new file mode 100644 index 0000000000000..3ed3572bf2319 --- /dev/null +++ b/test/pecoff/subsystem.test @@ -0,0 +1,12 @@ +# RUN: yaml2obj %p/Inputs/subsystem.main.yaml > %t.main.obj +# RUN: yaml2obj %p/Inputs/subsystem.winmain.yaml > %t.winmain.obj +# +# RUN: lld -flavor link /out:%t.main.exe -- %t.main.obj +# RUN: llvm-readobj -file-headers %t.main.exe | FileCheck -check-prefix=MAIN %s +# +# RUN: lld -flavor link /out:%t.winmain.exe -- %t.winmain.obj +# RUN: llvm-readobj -file-headers %t.winmain.exe | \ +# RUN: FileCheck -check-prefix=WINMAIN %s + +MAIN: Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI +WINMAIN: Subsystem: IMAGE_SUBSYSTEM_WINDOWS_GUI diff --git a/test/pecoff/tls.test b/test/pecoff/tls.test new file mode 100644 index 0000000000000..7e400ff60a0bd --- /dev/null +++ b/test/pecoff/tls.test @@ -0,0 +1,14 @@ +# RUN: yaml2obj %p/Inputs/hello.obj.yaml > %t1.obj +# RUN: yaml2obj %p/Inputs/tlsused.obj.yaml > %t2.obj + +# RUN: lld -flavor link /out:%t1.exe /subsystem:console /force -- %t1.obj +# RUN: llvm-readobj -file-headers %t1.exe | FileCheck -check-prefix=NOTLS %s + +# RUN: lld -flavor link /out:%t2.exe /subsystem:console /force -- %t1.obj %t2.obj +# RUN: llvm-readobj -file-headers %t2.exe | FileCheck -check-prefix=TLS %s + +NOTLS: TLSTableRVA: 0x0 +NOTLS: TLSTableSize: 0x0 + +TLS: TLSTableRVA: 0x1014 +TLS: TLSTableSize: 0x18 diff --git a/test/pecoff/trivial.test b/test/pecoff/trivial.test new file mode 100644 index 0000000000000..b1960f632813e --- /dev/null +++ b/test/pecoff/trivial.test @@ -0,0 +1,103 @@ +# Checks functionality of PECOFF writer. "nop.obj" is an object that has only +# text section. Other data, including data sections, relocations, symbol +# tables are not present in nop.obj. +# +# RUN: yaml2obj %p/Inputs/nop.obj.yaml > %t.obj +# +# RUN: lld -flavor link /out:%t1.exe /subsystem:console,3.11 /version:1.25 \ +# RUN: /entry:start /opt:noref -- %t.obj +# RUN: llvm-readobj -file-headers %t1.exe | FileCheck -check-prefix=FILE %s +# +# RUN: lld -flavor link /out:%t2.exe /subsystem:console /entry:start \ +# RUN: /opt:noref -- %t.obj +# RUN: llvm-readobj -sections %t2.exe | FileCheck -check-prefix=SECTIONS %s + +FILE: Format: COFF-i386 +FILE-NEXT: Arch: i386 +FILE-NEXT: AddressSize: 32bit +FILE-NEXT: ImageFileHeader { +FILE-NEXT: Machine: IMAGE_FILE_MACHINE_I386 (0x14C) +FILE-NEXT: SectionCount: 1 +FILE-NEXT: TimeDateStamp: +FILE-NEXT: PointerToSymbolTable: 0x0 +FILE-NEXT: SymbolCount: 0 +FILE-NEXT: OptionalHeaderSize: 224 +FILE-NEXT: Characteristics [ (0x102) +FILE-NEXT: IMAGE_FILE_32BIT_MACHINE (0x100) +FILE-NEXT: IMAGE_FILE_EXECUTABLE_IMAGE (0x2) +FILE-NEXT: ] +FILE-NEXT: } +FILE-NEXT: ImageOptionalHeader { +FILE-NEXT: MajorLinkerVersion: 0 +FILE-NEXT: MinorLinkerVersion: 0 +FILE-NEXT: SizeOfCode: 512 +FILE-NEXT: SizeOfInitializedData: 0 +FILE-NEXT: SizeOfUninitializedData: 0 +FILE-NEXT: AddressOfEntryPoint: 0x1000 +FILE-NEXT: BaseOfCode: 0x1000 +FILE-NEXT: BaseOfData: 0 +FILE-NEXT: ImageBase: 0x400000 +FILE-NEXT: SectionAlignment: 4096 +FILE-NEXT: FileAlignment: 512 +FILE-NEXT: MajorOperatingSystemVersion: 3 +FILE-NEXT: MinorOperatingSystemVersion: 11 +FILE-NEXT: MajorImageVersion: 1 +FILE-NEXT: MinorImageVersion: 25 +FILE-NEXT: MajorSubsystemVersion: 3 +FILE-NEXT: MinorSubsystemVersion: 11 +FILE-NEXT: SizeOfImage: 8192 +FILE-NEXT: SizeOfHeaders: 512 +FILE-NEXT: Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI (0x3) +FILE-NEXT: Characteristics [ (0x8540) +FILE-NEXT: IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE (0x40) +FILE-NEXT: IMAGE_DLL_CHARACTERISTICS_NO_SEH (0x400) +FILE-NEXT: IMAGE_DLL_CHARACTERISTICS_NX_COMPAT (0x100) +FILE-NEXT: IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE (0x8000) +FILE-NEXT: ] +FILE-NEXT: SizeOfStackReserve: 1048576 +FILE-NEXT: SizeOfStackCommit: 4096 +FILE-NEXT: SizeOfHeapReserve: 1048576 +FILE-NEXT: SizeOfHeapCommit: 4096 +FILE-NEXT: NumberOfRvaAndSize: 16 +FILE: DOSHeader { +FILE-NEXT: Magic: MZ +FILE-NEXT: UsedBytesInTheLastPage: 0 +FILE-NEXT: FileSizeInPages: 0 +FILE-NEXT: NumberOfRelocationItems: 0 +FILE-NEXT: HeaderSizeInParagraphs: 0 +FILE-NEXT: MinimumExtraParagraphs: 0 +FILE-NEXT: MaximumExtraParagraphs: 0 +FILE-NEXT: InitialRelativeSS: 0 +FILE-NEXT: InitialSP: 0 +FILE-NEXT: Checksum: 0 +FILE-NEXT: InitialIP: 0 +FILE-NEXT: InitialRelativeCS: 0 +FILE-NEXT: AddressOfRelocationTable: 64 +FILE-NEXT: OverlayNumber: 0 +FILE-NEXT: OEMid: 0 +FILE-NEXT: OEMinfo: 0 +FILE-NEXT: AddressOfNewExeHeader: 128 +FILE-NEXT: } + +SECTIONS: Format: COFF-i386 +SECTIONS-NEXT: Arch: i386 +SECTIONS-NEXT: AddressSize: 32bit +SECTIONS-NEXT: Sections [ +SECTIONS-NEXT: Section { +SECTIONS-NEXT: Number: 1 +SECTIONS-NEXT: Name: .text (2E 74 65 78 74 00 00 00) +SECTIONS-NEXT: VirtualSize: 0x6 +SECTIONS-NEXT: VirtualAddress: 0x1000 +SECTIONS-NEXT: RawDataSize: 512 +SECTIONS-NEXT: PointerToRawData: 0x200 +SECTIONS-NEXT: PointerToRelocations: 0x0 +SECTIONS-NEXT: PointerToLineNumbers: 0x0 +SECTIONS-NEXT: RelocationCount: 0 +SECTIONS-NEXT: LineNumberCount: 0 +SECTIONS-NEXT: Characteristics [ +SECTIONS-NEXT: IMAGE_SCN_CNT_CODE +SECTIONS-NEXT: IMAGE_SCN_MEM_EXECUTE +SECTIONS-NEXT: IMAGE_SCN_MEM_READ +SECTIONS-NEXT: ] +SECTIONS-NEXT: } +SECTIONS-NEXT: ] diff --git a/test/pecoff/unknown-drectve.test b/test/pecoff/unknown-drectve.test new file mode 100644 index 0000000000000..2c687c5d91639 --- /dev/null +++ b/test/pecoff/unknown-drectve.test @@ -0,0 +1,6 @@ +# RUN: yaml2obj %p/Inputs/unknown-drectve.obj.yaml > %t.obj +# +# RUN: not lld -flavor link /out:%t.exe -- %t.obj >& %t.log +# RUN: FileCheck -check-prefix=ERROR %s < %t.log + +ERROR: Cannot open /nosuchoption:foobar diff --git a/test/pecoff/weak-external.test b/test/pecoff/weak-external.test new file mode 100644 index 0000000000000..ff7492dcfd5e5 --- /dev/null +++ b/test/pecoff/weak-external.test @@ -0,0 +1,9 @@ +# RUN: yaml2obj %p/Inputs/weak-externals.obj.yaml > %t.obj + +# RUN: lld -flavor link /force /out:%t.exe /subsystem:console \ +# RUN: /entry:fn -- %t.obj %p/Inputs/static.lib 2> %t2.log +# RUN: FileCheck %s < %t2.log + +CHECK: _no_such_symbol1 +CHECK-NOT: _no_such_symbol2 +CHECK: _no_such_symbol3 diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 0000000000000..1151404441b43 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(linker-script-test) +add_subdirectory(lld) diff --git a/tools/Makefile b/tools/Makefile new file mode 100644 index 0000000000000..93d2a58c30682 --- /dev/null +++ b/tools/Makefile @@ -0,0 +1,17 @@ +##===- tools/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 := .. +LEVEL := $(LLD_LEVEL)/../.. + +include $(LLD_LEVEL)/../../Makefile.config + +PARALLEL_DIRS := linker-script-test lld + +include $(LLD_LEVEL)/Makefile diff --git a/tools/linker-script-test/CMakeLists.txt b/tools/linker-script-test/CMakeLists.txt new file mode 100644 index 0000000000000..2492f10aa80c1 --- /dev/null +++ b/tools/linker-script-test/CMakeLists.txt @@ -0,0 +1,8 @@ +add_llvm_executable(linker-script-test + linker-script-test.cpp + ) + +target_link_libraries(linker-script-test + LLVMSupport + lldReaderWriter + ) diff --git a/tools/linker-script-test/Makefile b/tools/linker-script-test/Makefile new file mode 100644 index 0000000000000..fd1b61accc07a --- /dev/null +++ b/tools/linker-script-test/Makefile @@ -0,0 +1,24 @@ +##===-------------- linker-script-test/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 := ../.. + +TOOLNAME = linker-script-test + +# No plugins, optimize startup time. +TOOL_NO_EXPORTS = 1 + +#include /Makefile.config +LEVEL := $(LLD_LEVEL)/../.. +include $(LEVEL)/Makefile.config + +LINK_COMPONENTS := $(TARGETS_TO_BUILD) +USEDLIBS = lldReaderWriter.a lldCore.a LLVMSupport.a + +include $(LLD_LEVEL)/Makefile diff --git a/tools/linker-script-test/linker-script-test.cpp b/tools/linker-script-test/linker-script-test.cpp new file mode 100644 index 0000000000000..027ecb36c3825 --- /dev/null +++ b/tools/linker-script-test/linker-script-test.cpp @@ -0,0 +1,57 @@ +//===- utils/linker-script-test/linker-script-test.cpp --------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Tool for testing linker script parsing. +/// +//===----------------------------------------------------------------------===// + +#include "lld/ReaderWriter/LinkerScript.h" + +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" + +using namespace llvm; +using namespace lld; +using namespace script; + +int main(int argc, const char **argv) { + llvm::sys::PrintStackTraceOnErrorSignal(); + llvm::PrettyStackTraceProgram X(argc, argv); + + { + ErrorOr<std::unique_ptr<MemoryBuffer>> mb = + MemoryBuffer::getFileOrSTDIN(argv[1]); + if (std::error_code ec = mb.getError()) { + llvm::errs() << ec.message() << "\n"; + return 1; + } + Lexer l(std::move(mb.get())); + Token tok; + while (true) { + l.lex(tok); + tok.dump(llvm::outs()); + if (tok._kind == Token::eof || tok._kind == Token::unknown) + break; + } + } + { + ErrorOr<std::unique_ptr<MemoryBuffer>> mb = + MemoryBuffer::getFileOrSTDIN(argv[1]); + if (std::error_code ec = mb.getError()) { + llvm::errs() << ec.message() << "\n"; + return 1; + } + Parser p(std::move(mb.get())); + if (!p.parse()) { + LinkerScript *ls = p.get(); + ls->dump(llvm::outs()); + } + } +} diff --git a/tools/lld/CMakeLists.txt b/tools/lld/CMakeLists.txt new file mode 100644 index 0000000000000..47f26b5300c21 --- /dev/null +++ b/tools/lld/CMakeLists.txt @@ -0,0 +1,25 @@ +add_llvm_executable(lld + lld.cpp + ) + +target_link_libraries(lld + lldDriver + LLVMSupport + ) + +install(TARGETS lld + RUNTIME DESTINATION bin) + +# Create the lld-link[.exe] symlink in the build directory. If symlink is not +# supported by the operating system, create a copy instead. +if(UNIX) + set(command create_symlink) + # Make relative symlink + set(src "lld${CMAKE_EXECUTABLE_SUFFIX}") +else() + set(command copy) + set(src "${LLVM_RUNTIME_OUTPUT_INTDIR}/lld${CMAKE_EXECUTABLE_SUFFIX}") +endif() +set(dst "${LLVM_RUNTIME_OUTPUT_INTDIR}/lld-link${CMAKE_EXECUTABLE_SUFFIX}") +add_custom_command(TARGET lld POST_BUILD + COMMAND ${CMAKE_COMMAND} -E ${command} ${src} ${dst}) diff --git a/tools/lld/Makefile b/tools/lld/Makefile new file mode 100644 index 0000000000000..77b0abbef9d0a --- /dev/null +++ b/tools/lld/Makefile @@ -0,0 +1,30 @@ +##===------- lld/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 := ../.. + +TOOLNAME = lld + +# No plugins, optimize startup time. +TOOL_NO_EXPORTS = 1 + +#include /Makefile.config +LEVEL := $(LLD_LEVEL)/../.. +include $(LEVEL)/Makefile.config + +LINK_COMPONENTS := $(TARGETS_TO_BUILD) +USEDLIBS = lldDriver.a lldConfig.a \ + lldELF.a lldMachO.a lldPECOFF.a lldYAML.a \ + lldReaderWriter.a lldCore.a lldNative.a \ + lldHexagonELFTarget.a lldMipsELFTarget.a \ + lldX86ELFTarget.a lldExampleSubTarget.a lldX86_64ELFTarget.a \ + lldAArch64ELFTarget.a lldARMELFTarget.a \ + LLVMOption.a + +include $(LLD_LEVEL)/Makefile diff --git a/tools/lld/TODO.txt b/tools/lld/TODO.txt new file mode 100644 index 0000000000000..20f8db0fcf109 --- /dev/null +++ b/tools/lld/TODO.txt @@ -0,0 +1,2 @@ +tools/lld +~~~~~~~~~ diff --git a/tools/lld/lld.cpp b/tools/lld/lld.cpp new file mode 100644 index 0000000000000..24c3a66a3ac3f --- /dev/null +++ b/tools/lld/lld.cpp @@ -0,0 +1,36 @@ +//===- tools/lld/lld.cpp - Linker Driver Dispatcher -----------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// This is the entry point to the lld driver. This is a thin wrapper which +/// dispatches to the given platform specific driver. +/// +//===----------------------------------------------------------------------===// + +#include "lld/Core/LLVM.h" +#include "lld/Driver/Driver.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" + +using namespace lld; + + +/// Universal linker main(). This linker eumulates the gnu, darwin, or +/// windows linker based on the tool name or if the first argument is +/// -flavor. +int main(int argc, const char *argv[]) { + // Standard set up, so program fails gracefully. + llvm::sys::PrintStackTraceOnErrorSignal(); + llvm::PrettyStackTraceProgram stackPrinter(argc, argv); + llvm::llvm_shutdown_obj shutdown; + + return UniversalDriver::link(argc, argv) ? 0 : 1; +} diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt new file mode 100644 index 0000000000000..bb651b5cfe621 --- /dev/null +++ b/unittests/CMakeLists.txt @@ -0,0 +1,15 @@ +add_custom_target(LLDUnitTests) +set_target_properties(LLDUnitTests PROPERTIES FOLDER "lld tests") + +# add_lld_unittest(test_dirname file1.cpp file2.cpp) +# +# Will compile the list of files together and link against lld +# Produces a binary named 'basename(test_dirname)'. +function(add_lld_unittest test_dirname) + add_unittest(LLDUnitTests ${test_dirname} ${ARGN}) + target_link_libraries(${test_dirname} ${LLVM_COMMON_LIBS}) +endfunction() + +add_subdirectory(CoreTests) +add_subdirectory(DriverTests) +add_subdirectory(MachOTests) diff --git a/unittests/CoreTests/CMakeLists.txt b/unittests/CoreTests/CMakeLists.txt new file mode 100644 index 0000000000000..aa85617916b43 --- /dev/null +++ b/unittests/CoreTests/CMakeLists.txt @@ -0,0 +1,4 @@ +add_lld_unittest(CoreTests + ParallelTest.cpp + RangeTest.cpp + ) diff --git a/unittests/CoreTests/Makefile b/unittests/CoreTests/Makefile new file mode 100644 index 0000000000000..366df01a08cc2 --- /dev/null +++ b/unittests/CoreTests/Makefile @@ -0,0 +1,14 @@ +##===- unittests/CoreTests/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 = ../.. +TESTNAME = CoreTest +LLVMLIBS = gtest.a LLVMOption.a LLVMSupport.a + +include $(LLD_LEVEL)/unittests/Makefile diff --git a/unittests/CoreTests/ParallelTest.cpp b/unittests/CoreTests/ParallelTest.cpp new file mode 100644 index 0000000000000..c0282437e2e1d --- /dev/null +++ b/unittests/CoreTests/ParallelTest.cpp @@ -0,0 +1,31 @@ +//===- lld/unittest/ParallelTest.cpp --------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Parallel.h unit tests. +/// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "lld/Core/Parallel.h" +#include <array> +#include <random> + +uint32_t array[1024 * 1024]; + +TEST(Parallel, sort) { + std::mt19937 randEngine; + std::uniform_int_distribution<uint32_t> dist; + + for (auto &i : array) + i = dist(randEngine); + + lld::parallel_sort(std::begin(array), std::end(array)); + ASSERT_TRUE(std::is_sorted(std::begin(array), std::end(array))); +} diff --git a/unittests/CoreTests/RangeTest.cpp b/unittests/CoreTests/RangeTest.cpp new file mode 100644 index 0000000000000..2b2fcd5f78753 --- /dev/null +++ b/unittests/CoreTests/RangeTest.cpp @@ -0,0 +1,240 @@ +//===- lld/unittest/RangeTest.cpp -----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief range.h unit tests. +/// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "lld/Core/range.h" +#include <array> +#include <assert.h> +#include <deque> +#include <forward_list> +#include <iterator> +#include <list> +#include <numeric> +#include <sstream> +#include <vector> + +template <typename T, typename U> struct AssertTypesSame; +template <typename T> struct AssertTypesSame<T, T> {}; +#define ASSERT_TYPES_SAME(T, U) AssertTypesSame<T, U>() + +struct no_begin {}; +struct member_begin { + int *begin(); +}; +struct free_begin {}; +int *begin(free_begin); + +template <typename T> +auto type_of_forward(T &&t) -> decltype(std::forward<T>(t)) { + return std::forward<T>(t); +} + +template <typename To> To implicit_cast(To val) { return val; } + +void test_traits() { + using namespace lld::detail; + ASSERT_TYPES_SAME(begin_result<no_begin>::type, undefined); + // This causes clang to segfault. +#if 0 + ASSERT_TYPES_SAME( + begin_result<decltype(type_of_forward(member_begin()))>::type, int *); +#endif + ASSERT_TYPES_SAME(begin_result<free_begin>::type, int *); +} + +TEST(Range, constructors) { + std::vector<int> v(5); + std::iota(v.begin(), v.end(), 0); + lld::range<std::vector<int>::iterator> r = v; + EXPECT_EQ(v.begin(), r.begin()); + EXPECT_EQ(v.end(), r.end()); + + int arr[] = { 1, 2, 3, 4, 5 }; + std::begin(arr); + lld::range<int *> r2 = arr; + EXPECT_EQ(5, r2.back()); +} + +TEST(Range, conversion_to_pointer_range) { + std::vector<int> v(5); + std::iota(v.begin(), v.end(), 0); + lld::range<int *> r = v; + EXPECT_EQ(&*v.begin(), r.begin()); + EXPECT_EQ(2, r[2]); +} + +template <typename Iter> void takes_range(lld::range<Iter> r) { + int expected = 0; + for (int val : r) { + EXPECT_EQ(expected++, val); + } +} + +void takes_ptr_range(lld::range<const int *> r) { + int expected = 0; + for (int val : r) { + EXPECT_EQ(expected++, val); + } +} + +TEST(Range, passing) { + using lld::make_range; + using lld::make_ptr_range; + std::list<int> l(5); + std::iota(l.begin(), l.end(), 0); + takes_range(make_range(l)); + takes_range(make_range(implicit_cast<const std::list<int> &>(l))); + std::deque<int> d(5); + std::iota(d.begin(), d.end(), 0); + takes_range(make_range(d)); + takes_range(make_range(implicit_cast<const std::deque<int> &>(d))); + std::vector<int> v(5); + std::iota(v.begin(), v.end(), 0); + takes_range(make_range(v)); + takes_range(make_range(implicit_cast<const std::vector<int> &>(v))); + static_assert( + std::is_same<decltype(make_ptr_range(v)), lld::range<int *>>::value, + "make_ptr_range should return a range of pointers"); + takes_range(make_ptr_range(v)); + takes_range(make_ptr_range(implicit_cast<const std::vector<int> &>(v))); + int arr[] = { 0, 1, 2, 3, 4 }; + takes_range(make_range(arr)); + const int carr[] = { 0, 1, 2, 3, 4 }; + takes_range(make_range(carr)); + + takes_ptr_range(v); + takes_ptr_range(implicit_cast<const std::vector<int> &>(v)); + takes_ptr_range(arr); + takes_ptr_range(carr); +} + +TEST(Range, access) { + std::array<int, 5> a = { { 1, 2, 3, 4, 5 } }; + lld::range<decltype(a.begin())> r = a; + EXPECT_EQ(4, r[3]); + EXPECT_EQ(4, r[-2]); +} + +template <bool b> struct CompileAssert; +template <> struct CompileAssert<true> {}; + +#if __has_feature(cxx_constexpr) +constexpr int arr[] = { 1, 2, 3, 4, 5 }; +TEST(Range, constexpr) { + constexpr lld::range<const int *> r(arr, arr + 5); + CompileAssert<r.front() == 1>(); + CompileAssert<r.size() == 5>(); + CompileAssert<r[4] == 5>(); +} +#endif + +template <typename Container> void test_slice() { + Container cont(10); + std::iota(cont.begin(), cont.end(), 0); + lld::range<decltype(cont.begin())> r = cont; + + // One argument. + EXPECT_EQ(10, r.slice(0).size()); + EXPECT_EQ(8, r.slice(2).size()); + EXPECT_EQ(2, r.slice(2).front()); + EXPECT_EQ(1, r.slice(-1).size()); + EXPECT_EQ(9, r.slice(-1).front()); + + // Two positive arguments. + EXPECT_TRUE(r.slice(5, 2).empty()); + EXPECT_EQ(next(cont.begin(), 5), r.slice(5, 2).begin()); + EXPECT_EQ(1, r.slice(1, 2).size()); + EXPECT_EQ(1, r.slice(1, 2).front()); + + // Two negative arguments. + EXPECT_TRUE(r.slice(-2, -5).empty()); + EXPECT_EQ(next(cont.begin(), 8), r.slice(-2, -5).begin()); + EXPECT_EQ(1, r.slice(-2, -1).size()); + EXPECT_EQ(8, r.slice(-2, -1).front()); + + // Positive start, negative stop. + EXPECT_EQ(1, r.slice(6, -3).size()); + EXPECT_EQ(6, r.slice(6, -3).front()); + EXPECT_TRUE(r.slice(6, -5).empty()); + EXPECT_EQ(next(cont.begin(), 6), r.slice(6, -5).begin()); + + // Negative start, positive stop. + EXPECT_TRUE(r.slice(-3, 6).empty()); + EXPECT_EQ(next(cont.begin(), 7), r.slice(-3, 6).begin()); + EXPECT_EQ(1, r.slice(-5, 6).size()); + EXPECT_EQ(5, r.slice(-5, 6).front()); +} + +TEST(Range, slice) { + // -fsanitize=undefined complains about this, but only if optimizations are + // enabled. +#if 0 + test_slice<std::forward_list<int>>(); +#endif + test_slice<std::list<int>>(); +// This doesn't build with libstdc++ 4.7 +#if 0 + test_slice<std::deque<int>>(); +#endif +} + +// This test is flaky and I've yet to pin down why. Changing between +// EXPECT_EQ(1, input.front()) and EXPECT_TRUE(input.front() == 1) makes it work +// with VS 2012 in Debug mode. Clang on Linux seems to fail with -03 and -02 -g +// -fsanitize=undefined. +#if 0 +TEST(Range, istream_range) { + std::istringstream stream("1 2 3 4 5"); + // MSVC interprets input as a function declaration if you don't declare start + // and instead directly pass std::istream_iterator<int>(stream). + auto start = std::istream_iterator<int>(stream); + lld::range<std::istream_iterator<int>> input( + start, std::istream_iterator<int>()); + EXPECT_TRUE(input.front() == 1); + input.pop_front(); + EXPECT_TRUE(input.front() == 2); + input.pop_front(2); + EXPECT_TRUE(input.front() == 4); + input.pop_front_upto(7); + EXPECT_TRUE(input.empty()); +} +#endif + +//! [algorithm using range] +template <typename T> void partial_sum(T &container) { + using lld::make_range; + auto range = make_range(container); + typename T::value_type sum = 0; + // One would actually use a range-based for loop + // in this case, but you get the idea: + for (; !range.empty(); range.pop_front()) { + sum += range.front(); + range.front() = sum; + } +} + +TEST(Range, user1) { + std::vector<int> v(5, 2); + partial_sum(v); + EXPECT_EQ(8, v[3]); +} +//! [algorithm using range] + +//! [algorithm using ptr_range] +void my_write(int fd, lld::range<const char *> buffer) {} + +TEST(Range, user2) { + std::string s("Hello world"); + my_write(1, s); +} diff --git a/unittests/DriverTests/CMakeLists.txt b/unittests/DriverTests/CMakeLists.txt new file mode 100644 index 0000000000000..59d56d459582f --- /dev/null +++ b/unittests/DriverTests/CMakeLists.txt @@ -0,0 +1,14 @@ +add_lld_unittest(DriverTests + UniversalDriverTest.cpp + GnuLdDriverTest.cpp + DarwinLdDriverTest.cpp + WinLinkDriverTest.cpp + WinLinkModuleDefTest.cpp + ) + +target_link_libraries(DriverTests + lldDriver + lldCore + lldPECOFF + lldMachO + ) diff --git a/unittests/DriverTests/DarwinLdDriverTest.cpp b/unittests/DriverTests/DarwinLdDriverTest.cpp new file mode 100644 index 0000000000000..1c77a05f58567 --- /dev/null +++ b/unittests/DriverTests/DarwinLdDriverTest.cpp @@ -0,0 +1,240 @@ +//===- lld/unittest/DarwinLdDriverTest.cpp --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Darwin's ld driver tests. +/// +//===----------------------------------------------------------------------===// + +#include "DriverTest.h" +#include "lld/ReaderWriter/MachOLinkingContext.h" +#include "llvm/Support/MachO.h" + +using namespace llvm; +using namespace lld; + +namespace { +class DarwinLdParserTest + : public ParserTest<DarwinLdDriver, MachOLinkingContext> { +protected: + const LinkingContext *linkingContext() override { return &_ctx; } +}; +} + +TEST_F(DarwinLdParserTest, Basic) { + EXPECT_TRUE(parse("ld", "foo.o", "bar.o", "-arch", "i386", nullptr)); + EXPECT_FALSE(_ctx.allowRemainingUndefines()); + EXPECT_FALSE(_ctx.deadStrip()); + EXPECT_EQ(2, inputFileCount()); + EXPECT_EQ("foo.o", inputFile(0)); + EXPECT_EQ("bar.o", inputFile(1)); +} + +TEST_F(DarwinLdParserTest, Output) { + EXPECT_TRUE(parse("ld", "-o", "my.out", "foo.o", "-arch", "i386", nullptr)); + EXPECT_EQ("my.out", _ctx.outputPath()); +} + +TEST_F(DarwinLdParserTest, Dylib) { + EXPECT_TRUE(parse("ld", "-dylib", "foo.o", "-arch", "i386", nullptr)); + EXPECT_EQ(llvm::MachO::MH_DYLIB, _ctx.outputMachOType()); +} + +TEST_F(DarwinLdParserTest, Relocatable) { + EXPECT_TRUE(parse("ld", "-r", "foo.o", "-arch", "i386", nullptr)); + EXPECT_EQ(llvm::MachO::MH_OBJECT, _ctx.outputMachOType()); +} + +TEST_F(DarwinLdParserTest, Bundle) { + EXPECT_TRUE(parse("ld", "-bundle", "foo.o", "-arch", "i386", nullptr)); + EXPECT_EQ(llvm::MachO::MH_BUNDLE, _ctx.outputMachOType()); +} + +TEST_F(DarwinLdParserTest, Preload) { + EXPECT_TRUE(parse("ld", "-preload", "foo.o", "-arch", "i386", nullptr)); + EXPECT_EQ(llvm::MachO::MH_PRELOAD, _ctx.outputMachOType()); +} + +TEST_F(DarwinLdParserTest, Static) { + EXPECT_TRUE(parse("ld", "-static", "foo.o", "-arch", "i386", nullptr)); + EXPECT_EQ(llvm::MachO::MH_EXECUTE, _ctx.outputMachOType()); +} + +TEST_F(DarwinLdParserTest, Entry) { + EXPECT_TRUE(parse("ld", "-e", "entryFunc", "foo.o", "-arch", "i386",nullptr)); + EXPECT_EQ("entryFunc", _ctx.entrySymbolName()); +} + +TEST_F(DarwinLdParserTest, DeadStrip) { + EXPECT_TRUE(parse("ld", "-arch", "x86_64", "-dead_strip", "foo.o", nullptr)); + EXPECT_TRUE(_ctx.deadStrip()); +} + +TEST_F(DarwinLdParserTest, DeadStripRootsExe) { + EXPECT_TRUE(parse("ld", "-arch", "x86_64", "-dead_strip", "foo.o", nullptr)); + EXPECT_FALSE(_ctx.globalsAreDeadStripRoots()); +} + +TEST_F(DarwinLdParserTest, DeadStripRootsDylib) { + EXPECT_TRUE(parse("ld", "-arch", "x86_64", "-dylib", "-dead_strip", "foo.o", + nullptr)); + EXPECT_TRUE(_ctx.globalsAreDeadStripRoots()); +} + +TEST_F(DarwinLdParserTest, Arch) { + EXPECT_TRUE(parse("ld", "-arch", "x86_64", "foo.o", nullptr)); + EXPECT_EQ(MachOLinkingContext::arch_x86_64, _ctx.arch()); + EXPECT_EQ((uint32_t)llvm::MachO::CPU_TYPE_X86_64, _ctx.getCPUType()); + EXPECT_EQ(llvm::MachO::CPU_SUBTYPE_X86_64_ALL, _ctx.getCPUSubType()); +} + +TEST_F(DarwinLdParserTest, Arch_x86) { + EXPECT_TRUE(parse("ld", "-arch", "i386", "foo.o", nullptr)); + EXPECT_EQ(MachOLinkingContext::arch_x86, _ctx.arch()); + EXPECT_EQ((uint32_t)llvm::MachO::CPU_TYPE_I386, _ctx.getCPUType()); + EXPECT_EQ(llvm::MachO::CPU_SUBTYPE_X86_ALL, _ctx.getCPUSubType()); +} + +TEST_F(DarwinLdParserTest, Arch_armv6) { + EXPECT_TRUE(parse("ld", "-arch", "armv6", "foo.o", nullptr)); + EXPECT_EQ(MachOLinkingContext::arch_armv6, _ctx.arch()); + EXPECT_EQ((uint32_t)llvm::MachO::CPU_TYPE_ARM, _ctx.getCPUType()); + EXPECT_EQ(llvm::MachO::CPU_SUBTYPE_ARM_V6, _ctx.getCPUSubType()); +} + +TEST_F(DarwinLdParserTest, Arch_armv7) { + EXPECT_TRUE(parse("ld", "-arch", "armv7", "foo.o", nullptr)); + EXPECT_EQ(MachOLinkingContext::arch_armv7, _ctx.arch()); + EXPECT_EQ((uint32_t)llvm::MachO::CPU_TYPE_ARM, _ctx.getCPUType()); + EXPECT_EQ(llvm::MachO::CPU_SUBTYPE_ARM_V7, _ctx.getCPUSubType()); +} + +TEST_F(DarwinLdParserTest, Arch_armv7s) { + EXPECT_TRUE(parse("ld", "-arch", "armv7s", "foo.o", nullptr)); + EXPECT_EQ(MachOLinkingContext::arch_armv7s, _ctx.arch()); + EXPECT_EQ((uint32_t)llvm::MachO::CPU_TYPE_ARM, _ctx.getCPUType()); + EXPECT_EQ(llvm::MachO::CPU_SUBTYPE_ARM_V7S, _ctx.getCPUSubType()); +} + +TEST_F(DarwinLdParserTest, MinMacOSX10_7) { + EXPECT_TRUE(parse("ld", "-macosx_version_min", "10.7", "foo.o", + "-arch", "x86_64", nullptr)); + EXPECT_EQ(MachOLinkingContext::OS::macOSX, _ctx.os()); + EXPECT_TRUE(_ctx.minOS("10.7", "")); + EXPECT_FALSE(_ctx.minOS("10.8", "")); +} + +TEST_F(DarwinLdParserTest, MinMacOSX10_8) { + EXPECT_TRUE(parse("ld", "-macosx_version_min", "10.8.3", "foo.o", + "-arch", "x86_64", nullptr)); + EXPECT_EQ(MachOLinkingContext::OS::macOSX, _ctx.os()); + EXPECT_TRUE(_ctx.minOS("10.7", "")); + EXPECT_TRUE(_ctx.minOS("10.8", "")); +} + +TEST_F(DarwinLdParserTest, iOS5) { + EXPECT_TRUE(parse("ld", "-ios_version_min", "5.0", "foo.o", + "-arch", "armv7", nullptr)); + EXPECT_EQ(MachOLinkingContext::OS::iOS, _ctx.os()); + EXPECT_TRUE(_ctx.minOS("", "5.0")); + EXPECT_FALSE(_ctx.minOS("", "6.0")); +} + +TEST_F(DarwinLdParserTest, iOS6) { + EXPECT_TRUE(parse("ld", "-ios_version_min", "6.0", "foo.o", "-arch", "armv7", + nullptr)); + EXPECT_EQ(MachOLinkingContext::OS::iOS, _ctx.os()); + EXPECT_TRUE(_ctx.minOS("", "5.0")); + EXPECT_TRUE(_ctx.minOS("", "6.0")); +} + +TEST_F(DarwinLdParserTest, iOS_Simulator5) { + EXPECT_TRUE(parse("ld", "-ios_simulator_version_min", "5.0", "a.o", + "-arch", "i386", nullptr)); + EXPECT_EQ(MachOLinkingContext::OS::iOS_simulator, _ctx.os()); + EXPECT_TRUE(_ctx.minOS("", "5.0")); + EXPECT_FALSE(_ctx.minOS("", "6.0")); +} + +TEST_F(DarwinLdParserTest, iOS_Simulator6) { + EXPECT_TRUE(parse("ld", "-ios_simulator_version_min", "6.0", "a.o", + "-arch", "i386", nullptr)); + EXPECT_EQ(MachOLinkingContext::OS::iOS_simulator, _ctx.os()); + EXPECT_TRUE(_ctx.minOS("", "5.0")); + EXPECT_TRUE(_ctx.minOS("", "6.0")); +} + +TEST_F(DarwinLdParserTest, compatibilityVersion) { + EXPECT_TRUE( + parse("ld", "-dylib", "-compatibility_version", "1.2.3", "a.o", + "-arch", "i386",nullptr)); + EXPECT_EQ(_ctx.compatibilityVersion(), 0x10203U); +} + +TEST_F(DarwinLdParserTest, compatibilityVersionInvalidType) { + EXPECT_FALSE(parse("ld", "-bundle", "-compatibility_version", "1.2.3", "a.o", + "-arch", "i386",nullptr)); +} + +TEST_F(DarwinLdParserTest, compatibilityVersionInvalidValue) { + EXPECT_FALSE(parse("ld", "-bundle", "-compatibility_version", "1,2,3", "a.o", + "-arch", "i386", nullptr)); +} + +TEST_F(DarwinLdParserTest, currentVersion) { + EXPECT_TRUE( + parse("ld", "-dylib", "-current_version", "1.2.3", "a.o", "-arch", "i386", + nullptr)); + EXPECT_EQ(_ctx.currentVersion(), 0x10203U); +} + +TEST_F(DarwinLdParserTest, currentVersionInvalidType) { + EXPECT_FALSE( + parse("ld", "-bundle", "-current_version", "1.2.3", "a.o", + "-arch", "i386", nullptr)); +} + +TEST_F(DarwinLdParserTest, currentVersionInvalidValue) { + EXPECT_FALSE( + parse("ld", "-bundle", "-current_version", "1,2,3", "a.o", + "-arch", "i386", nullptr)); +} + +TEST_F(DarwinLdParserTest, bundleLoader) { + EXPECT_TRUE( + parse("ld", "-bundle", "-bundle_loader", "/bin/ls", "a.o", + "-arch", "i386", nullptr)); + EXPECT_EQ(_ctx.bundleLoader(), "/bin/ls"); +} + +TEST_F(DarwinLdParserTest, bundleLoaderInvalidType) { + EXPECT_FALSE(parse("ld", "-bundle_loader", "/bin/ls", "a.o", "-arch", "i386", + nullptr)); +} + +TEST_F(DarwinLdParserTest, deadStrippableDylib) { + EXPECT_TRUE( + parse("ld", "-dylib", "-mark_dead_strippable_dylib", "a.o", + "-arch", "i386", nullptr)); + EXPECT_EQ(true, _ctx.deadStrippableDylib()); +} + +TEST_F(DarwinLdParserTest, deadStrippableDylibInvalidType) { + EXPECT_FALSE(parse("ld", "-mark_dead_strippable_dylib", "a.o", + "-arch", "i386", nullptr)); +} + +TEST_F(DarwinLdParserTest, llvmOptions) { + EXPECT_TRUE(parse("ld", "-mllvm", "-debug-only", "-mllvm", "foo", "a.o", + "-arch", "i386", nullptr)); + const std::vector<const char *> &options = _ctx.llvmOptions(); + EXPECT_EQ(options.size(), 2UL); + EXPECT_EQ(strcmp(options[0],"-debug-only"), 0); + EXPECT_EQ(strcmp(options[1],"foo"), 0); +} diff --git a/unittests/DriverTests/DriverTest.h b/unittests/DriverTests/DriverTest.h new file mode 100644 index 0000000000000..2349132ee2ce7 --- /dev/null +++ b/unittests/DriverTests/DriverTest.h @@ -0,0 +1,61 @@ +//===- lld/unittest/DriverTest.h ------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lld/Driver/Driver.h" +#include "llvm/Support/raw_ostream.h" +#include "gtest/gtest.h" +#include <stdarg.h> + +namespace { + +using namespace llvm; +using namespace lld; + +template<typename D, typename T> +class ParserTest : public testing::Test { +protected: + + virtual const LinkingContext *linkingContext() = 0; + + std::string &errorMessage() { return _errorMessage; } + + // Convenience method for getting number of input files. + int inputFileCount() { + return linkingContext()->getNodes().size(); + } + + // Convenience method for getting i'th input files name. + std::string inputFile(int index) { + Node &node = *linkingContext()->getNodes()[index]; + if (node.kind() == Node::Kind::File) + return cast<FileNode>(&node)->getFile()->path(); + llvm_unreachable("not handling other types of input files"); + } + + // For unit tests to call driver with various command lines. + bool parse(const char *args, ...) { + // Construct command line options from varargs. + std::vector<const char *> vec; + vec.push_back(args); + va_list ap; + va_start(ap, args); + while (const char *arg = va_arg(ap, const char *)) + vec.push_back(arg); + va_end(ap); + + // Call the parser. + raw_string_ostream os(_errorMessage); + return D::parse(vec.size(), &vec[0], _ctx, os); + } + + T _ctx; + std::string _errorMessage; +}; + +} diff --git a/unittests/DriverTests/GnuLdDriverTest.cpp b/unittests/DriverTests/GnuLdDriverTest.cpp new file mode 100644 index 0000000000000..92eb920f01801 --- /dev/null +++ b/unittests/DriverTests/GnuLdDriverTest.cpp @@ -0,0 +1,284 @@ +//===- lld/unittest/GnuLdDriverTest.cpp -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief GNU ld driver tests. +/// +//===----------------------------------------------------------------------===// + +#include "DriverTest.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "llvm/Support/MemoryBuffer.h" + +using namespace llvm; +using namespace lld; + +namespace { + +class GnuLdParserTest + : public ParserTest<GnuLdDriver, std::unique_ptr<ELFLinkingContext>> { +protected: + const LinkingContext *linkingContext() override { return _ctx.get(); } +}; + +class LinkerScriptTest : public testing::Test { +protected: + void SetUp() override { + llvm::Triple triple(llvm::sys::getDefaultTargetTriple()); + _ctx = std::move(GnuLdDriver::createELFLinkingContext(triple)); + } + + void parse(StringRef script, bool nostdlib = false) { + std::unique_ptr<MemoryBuffer> mb = MemoryBuffer::getMemBuffer( + script, "foo.so"); + std::string s; + raw_string_ostream out(s); + std::error_code ec = + GnuLdDriver::evalLinkerScript(*_ctx, std::move(mb), out, nostdlib); + EXPECT_FALSE(ec); + }; + + std::unique_ptr<ELFLinkingContext> _ctx; +}; + +} // anonymous namespace + +TEST_F(GnuLdParserTest, Empty) { + EXPECT_FALSE(parse("ld", nullptr)); + EXPECT_EQ(linkingContext(), nullptr); + EXPECT_EQ("No input files\n", errorMessage()); +} + +// -o + +TEST_F(GnuLdParserTest, Output) { + EXPECT_TRUE(parse("ld", "a.o", "-o", "foo", nullptr)); + EXPECT_EQ("foo", _ctx->outputPath()); +} + +TEST_F(GnuLdParserTest, OutputDefault) { + EXPECT_TRUE(parse("ld", "abc.o", nullptr)); + EXPECT_EQ("a.out", _ctx->outputPath()); +} + +// --noinhibit-exec + +TEST_F(GnuLdParserTest, NoinhibitExec) { + EXPECT_TRUE(parse("ld", "a.o", "--noinhibit-exec", nullptr)); + EXPECT_TRUE(_ctx->allowRemainingUndefines()); +} + +// --entry + +TEST_F(GnuLdParserTest, Entry) { + EXPECT_TRUE(parse("ld", "a.o", "--entry", "foo", nullptr)); + EXPECT_EQ("foo", _ctx->entrySymbolName()); +} + +TEST_F(GnuLdParserTest, EntryShort) { + EXPECT_TRUE(parse("ld", "a.o", "-e", "foo", nullptr)); + EXPECT_EQ("foo", _ctx->entrySymbolName()); +} + +TEST_F(GnuLdParserTest, EntryJoined) { + EXPECT_TRUE(parse("ld", "a.o", "--entry=foo", nullptr)); + EXPECT_EQ("foo", _ctx->entrySymbolName()); +} + +// --export-dynamic + +TEST_F(GnuLdParserTest, ExportDynamic) { + EXPECT_TRUE(parse("ld", "a.o", "--export-dynamic", nullptr)); + EXPECT_TRUE(_ctx->shouldExportDynamic()); +} + +TEST_F(GnuLdParserTest, NoExportDynamic) { + EXPECT_TRUE(parse("ld", "a.o", "--no-export-dynamic", nullptr)); + EXPECT_FALSE(_ctx->shouldExportDynamic()); +} + +// --init + +TEST_F(GnuLdParserTest, Init) { + EXPECT_TRUE(parse("ld", "a.o", "-init", "foo", "-init", "bar", nullptr)); + EXPECT_EQ("bar", _ctx->initFunction()); +} + +TEST_F(GnuLdParserTest, InitJoined) { + EXPECT_TRUE(parse("ld", "a.o", "-init=foo", nullptr)); + EXPECT_EQ("foo", _ctx->initFunction()); +} + +// --soname + +TEST_F(GnuLdParserTest, SOName) { + EXPECT_TRUE(parse("ld", "a.o", "--soname=foo", nullptr)); + EXPECT_EQ("foo", _ctx->sharedObjectName()); +} + +TEST_F(GnuLdParserTest, SONameSingleDash) { + EXPECT_TRUE(parse("ld", "a.o", "-soname=foo", nullptr)); + EXPECT_EQ("foo", _ctx->sharedObjectName()); +} + +TEST_F(GnuLdParserTest, SONameH) { + EXPECT_TRUE(parse("ld", "a.o", "-h", "foo", nullptr)); + EXPECT_EQ("foo", _ctx->sharedObjectName()); +} + +// -rpath + +TEST_F(GnuLdParserTest, Rpath) { + EXPECT_TRUE(parse("ld", "a.o", "-rpath", "foo:bar", nullptr)); + EXPECT_EQ(2, _ctx->getRpathList().size()); + EXPECT_EQ("foo", _ctx->getRpathList()[0]); + EXPECT_EQ("bar", _ctx->getRpathList()[1]); +} + +TEST_F(GnuLdParserTest, RpathEq) { + EXPECT_TRUE(parse("ld", "a.o", "-rpath=foo", nullptr)); + EXPECT_EQ(1, _ctx->getRpathList().size()); + EXPECT_EQ("foo", _ctx->getRpathList()[0]); +} + +// --defsym + +TEST_F(GnuLdParserTest, DefsymDecimal) { + EXPECT_TRUE(parse("ld", "a.o", "--defsym=sym=1000", nullptr)); + assert(_ctx.get()); + auto map = _ctx->getAbsoluteSymbols(); + EXPECT_EQ((size_t)1, map.size()); + EXPECT_EQ((uint64_t)1000, map["sym"]); +} + +TEST_F(GnuLdParserTest, DefsymHexadecimal) { + EXPECT_TRUE(parse("ld", "a.o", "--defsym=sym=0x1000", nullptr)); + auto map = _ctx->getAbsoluteSymbols(); + EXPECT_EQ((size_t)1, map.size()); + EXPECT_EQ((uint64_t)0x1000, map["sym"]); +} + +TEST_F(GnuLdParserTest, DefsymAlias) { + EXPECT_TRUE(parse("ld", "a.o", "--defsym=sym=abc", nullptr)); + auto map = _ctx->getAliases(); + EXPECT_EQ((size_t)1, map.size()); + EXPECT_EQ("abc", map["sym"]); +} + +TEST_F(GnuLdParserTest, DefsymOctal) { + EXPECT_TRUE(parse("ld", "a.o", "--defsym=sym=0777", nullptr)); + auto map = _ctx->getAbsoluteSymbols(); + EXPECT_EQ((size_t)1, map.size()); + EXPECT_EQ((uint64_t)0777, map["sym"]); +} + +TEST_F(GnuLdParserTest, DefsymMisssingSymbol) { + EXPECT_FALSE(parse("ld", "a.o", "--defsym==0", nullptr)); +} + +TEST_F(GnuLdParserTest, DefsymMisssingValue) { + EXPECT_FALSE(parse("ld", "a.o", "--defsym=sym=", nullptr)); +} + +// --as-needed + +TEST_F(GnuLdParserTest, AsNeeded) { + EXPECT_TRUE(parse("ld", "a.o", "--as-needed", "b.o", "c.o", + "--no-as-needed", "d.o", nullptr)); + std::vector<std::unique_ptr<Node>> &nodes = _ctx->getNodes(); + EXPECT_EQ((size_t)4, nodes.size()); + EXPECT_FALSE(cast<FileNode>(nodes[0].get())->asNeeded()); + EXPECT_TRUE(cast<FileNode>(nodes[1].get())->asNeeded()); + EXPECT_TRUE(cast<FileNode>(nodes[2].get())->asNeeded()); + EXPECT_FALSE(cast<FileNode>(nodes[3].get())->asNeeded()); +} + +// Linker script + +TEST_F(LinkerScriptTest, Input) { + parse("INPUT(/x /y)"); + std::vector<std::unique_ptr<Node>> &nodes = _ctx->getNodes(); + EXPECT_EQ((size_t)2, nodes.size()); + EXPECT_EQ("/x", cast<FileNode>(nodes[0].get())->getFile()->path()); + EXPECT_EQ("/y", cast<FileNode>(nodes[1].get())->getFile()->path()); +} + +TEST_F(LinkerScriptTest, Group) { + parse("GROUP(/x /y)"); + std::vector<std::unique_ptr<Node>> &nodes = _ctx->getNodes(); + EXPECT_EQ((size_t)3, nodes.size()); + EXPECT_EQ("/x", cast<FileNode>(nodes[0].get())->getFile()->path()); + EXPECT_EQ("/y", cast<FileNode>(nodes[1].get())->getFile()->path()); + EXPECT_EQ(2, cast<GroupEnd>(nodes[2].get())->getSize()); +} + +TEST_F(LinkerScriptTest, SearchDir) { + parse("SEARCH_DIR(\"/foo/bar\")"); + std::vector<StringRef> paths = _ctx->getSearchPaths(); + EXPECT_EQ((size_t)1, paths.size()); + EXPECT_EQ("/foo/bar", paths[0]); +} + +TEST_F(LinkerScriptTest, Entry) { + parse("ENTRY(blah)"); + EXPECT_EQ("blah", _ctx->entrySymbolName()); +} + +TEST_F(LinkerScriptTest, Output) { + parse("OUTPUT(\"/path/to/output\")"); + EXPECT_EQ("/path/to/output", _ctx->outputPath()); +} + +// Test that search paths are ignored when nostdlib is set. +TEST_F(LinkerScriptTest, IgnoreSearchDirNoStdLib) { + parse("SEARCH_DIR(\"/foo/bar\")", true /*nostdlib*/); + std::vector<StringRef> paths = _ctx->getSearchPaths(); + EXPECT_EQ((size_t)0, paths.size()); +} + +TEST_F(LinkerScriptTest, ExprEval) { + parse("SECTIONS { symbol = 0x4000 + 0x40; \n" + ". = (symbol >= 0x4040)? (0x5001 * 2 & 0xFFF0) << 1 : 0}"); + + EXPECT_EQ((size_t)1, _ctx->linkerScriptSema().getLinkerScripts().size()); + + script::LinkerScript *ls = + _ctx->linkerScriptSema().getLinkerScripts()[0]->get(); + EXPECT_EQ((size_t)1, ls->_commands.size()); + + auto *secs = dyn_cast<const script::Sections>(*ls->_commands.begin()); + EXPECT_TRUE(secs != nullptr); + EXPECT_EQ(2, secs->end() - secs->begin()); + + auto command = secs->begin(); + auto *sa1 = dyn_cast<const script::SymbolAssignment>(*command); + EXPECT_TRUE(sa1 != nullptr); + EXPECT_EQ(script::SymbolAssignment::Simple, sa1->assignmentKind()); + EXPECT_EQ(script::SymbolAssignment::Default, sa1->assignmentVisibility()); + + ++command; + auto *sa2 = dyn_cast<const script::SymbolAssignment>(*command); + EXPECT_TRUE(sa2 != nullptr); + EXPECT_EQ(script::SymbolAssignment::Simple, sa2->assignmentKind()); + EXPECT_EQ(script::SymbolAssignment::Default, sa2->assignmentVisibility()); + + script::Expression::SymbolTableTy mySymbolTable; + auto ans = sa1->expr()->evalExpr(mySymbolTable); + EXPECT_FALSE(ans.getError()); + int64_t result = *ans; + EXPECT_EQ(0x4040, result); + mySymbolTable[sa1->symbol()] = result; + + auto ans2 = sa2->expr()->evalExpr(mySymbolTable); + EXPECT_FALSE(ans2.getError()); + result = *ans2; + EXPECT_EQ(0x14000, result); + EXPECT_EQ(0, sa2->symbol().compare(StringRef("."))); +} + diff --git a/unittests/DriverTests/Makefile b/unittests/DriverTests/Makefile new file mode 100644 index 0000000000000..ae97fb01adbf3 --- /dev/null +++ b/unittests/DriverTests/Makefile @@ -0,0 +1,20 @@ +##===---- unittests/DriverTests/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 = ../.. +TESTNAME = DriverTests +USEDLIBS = lldDriver.a lldConfig.a \ + lldELF.a lldMachO.a lldPECOFF.a \ + lldCore.a lldNative.a lldReaderWriter.a \ + lldHexagonELFTarget.a lldMipsELFTarget.a \ + lldX86ELFTarget.a lldExampleSubTarget.a lldX86_64ELFTarget.a \ + lldYAML.a lldAArch64ELFTarget.a lldARMELFTarget.a \ + LLVMObject.a LLVMMCParser.a LLVMMC.a LLVMBitReader.a \ + LLVMCore.a LLVMOption.a LLVMSupport.a +include $(LLD_LEVEL)/unittests/Makefile diff --git a/unittests/DriverTests/UniversalDriverTest.cpp b/unittests/DriverTests/UniversalDriverTest.cpp new file mode 100644 index 0000000000000..8e90ca4d58673 --- /dev/null +++ b/unittests/DriverTests/UniversalDriverTest.cpp @@ -0,0 +1,33 @@ +//===- lld/unittest/UniversalDriverTest.cpp -------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Universal driver tests that depend on the value of argv[0]. +/// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "lld/Driver/Driver.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace lld; + +TEST(UniversalDriver, flavor) { + const char *args[] = { "gnu-ld" }; + + std::string diags; + raw_string_ostream os(diags); + UniversalDriver::link(array_lengthof(args), args, os); + EXPECT_EQ(os.str().find("failed to determine driver flavor"), + std::string::npos); + EXPECT_NE(os.str().find("No input files"), + std::string::npos); +} diff --git a/unittests/DriverTests/WinLinkDriverTest.cpp b/unittests/DriverTests/WinLinkDriverTest.cpp new file mode 100644 index 0000000000000..c2bc455aa81f5 --- /dev/null +++ b/unittests/DriverTests/WinLinkDriverTest.cpp @@ -0,0 +1,728 @@ +//===- lld/unittest/WinLinkDriverTest.cpp ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Windows link.exe driver tests. +/// +//===----------------------------------------------------------------------===// + +#include "DriverTest.h" +#include "lld/ReaderWriter/PECOFFLinkingContext.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Support/COFF.h" +#include <set> +#include <vector> + +using namespace llvm; +using namespace lld; + +namespace { +class WinLinkParserTest + : public ParserTest<WinLinkDriver, PECOFFLinkingContext> { +protected: + const LinkingContext *linkingContext() override { return &_ctx; } +}; +} + +TEST_F(WinLinkParserTest, Basic) { + EXPECT_TRUE(parse("link.exe", "/subsystem:console", "/out:a.exe", + "-entry:start", "a.obj", "b.obj", "c.obj", nullptr)); + EXPECT_EQ(llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI, _ctx.getSubsystem()); + EXPECT_EQ(llvm::COFF::IMAGE_FILE_MACHINE_I386, _ctx.getMachineType()); + EXPECT_EQ("a.exe", _ctx.outputPath()); + EXPECT_EQ("start", _ctx.getEntrySymbolName()); + EXPECT_EQ(4, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); + EXPECT_EQ("b.obj", inputFile(1)); + EXPECT_EQ("c.obj", inputFile(2)); + EXPECT_TRUE(_ctx.getInputSearchPaths().empty()); + + // Unspecified flags will have default values. + EXPECT_FALSE(_ctx.isDll()); + EXPECT_EQ(6, _ctx.getMinOSVersion().majorVersion); + EXPECT_EQ(0, _ctx.getMinOSVersion().minorVersion); + EXPECT_EQ(0x400000U, _ctx.getBaseAddress()); + EXPECT_EQ(1024 * 1024U, _ctx.getStackReserve()); + EXPECT_EQ(4096U, _ctx.getStackCommit()); + EXPECT_EQ(4096U, _ctx.getSectionDefaultAlignment()); + EXPECT_FALSE(_ctx.allowRemainingUndefines()); + EXPECT_TRUE(_ctx.isNxCompat()); + EXPECT_FALSE(_ctx.getLargeAddressAware()); + EXPECT_TRUE(_ctx.getAllowBind()); + EXPECT_TRUE(_ctx.getAllowIsolation()); + EXPECT_FALSE(_ctx.getSwapRunFromCD()); + EXPECT_FALSE(_ctx.getSwapRunFromNet()); + EXPECT_TRUE(_ctx.getBaseRelocationEnabled()); + EXPECT_TRUE(_ctx.isTerminalServerAware()); + EXPECT_TRUE(_ctx.getDynamicBaseEnabled()); + EXPECT_TRUE(_ctx.getCreateManifest()); + EXPECT_EQ("", _ctx.getManifestDependency()); + EXPECT_FALSE(_ctx.getEmbedManifest()); + EXPECT_EQ(1, _ctx.getManifestId()); + EXPECT_TRUE(_ctx.getManifestUAC()); + EXPECT_EQ("'asInvoker'", _ctx.getManifestLevel()); + EXPECT_EQ("'false'", _ctx.getManifestUiAccess()); + EXPECT_TRUE(_ctx.deadStrip()); + EXPECT_FALSE(_ctx.logInputFiles()); +} + +TEST_F(WinLinkParserTest, StartsWithHyphen) { + EXPECT_TRUE( + parse("link.exe", "-subsystem:console", "-out:a.exe", "a.obj", nullptr)); + EXPECT_EQ(llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI, _ctx.getSubsystem()); + EXPECT_EQ("a.exe", _ctx.outputPath()); + EXPECT_EQ(2, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); +} + +TEST_F(WinLinkParserTest, UppercaseOption) { + EXPECT_TRUE( + parse("link.exe", "/SUBSYSTEM:CONSOLE", "/OUT:a.exe", "a.obj", nullptr)); + EXPECT_EQ(llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI, _ctx.getSubsystem()); + EXPECT_EQ("a.exe", _ctx.outputPath()); + EXPECT_EQ(2, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); +} + +TEST_F(WinLinkParserTest, Mllvm) { + EXPECT_TRUE(parse("link.exe", "/mllvm:-debug", "a.obj", nullptr)); + const std::vector<const char *> &options = _ctx.llvmOptions(); + EXPECT_EQ(1U, options.size()); + EXPECT_STREQ("-debug", options[0]); +} + +TEST_F(WinLinkParserTest, NoInputFiles) { + EXPECT_FALSE(parse("link.exe", nullptr)); + EXPECT_EQ("No input files\n", errorMessage()); +} + +// +// Tests for implicit file extension interpolation. +// + +TEST_F(WinLinkParserTest, NoFileExtension) { + EXPECT_TRUE(parse("link.exe", "foo", "bar", nullptr)); + EXPECT_EQ("foo.exe", _ctx.outputPath()); + EXPECT_EQ(3, inputFileCount()); + EXPECT_EQ("foo.obj", inputFile(0)); + EXPECT_EQ("bar.obj", inputFile(1)); +} + +TEST_F(WinLinkParserTest, NonStandardFileExtension) { + EXPECT_TRUE(parse("link.exe", "foo.o", nullptr)); + EXPECT_EQ("foo.exe", _ctx.outputPath()); + EXPECT_EQ(2, inputFileCount()); + EXPECT_EQ("foo.o", inputFile(0)); +} + +TEST_F(WinLinkParserTest, Libpath) { + EXPECT_TRUE( + parse("link.exe", "/libpath:dir1", "/libpath:dir2", "a.obj", nullptr)); + const std::vector<StringRef> &paths = _ctx.getInputSearchPaths(); + EXPECT_EQ(2U, paths.size()); + EXPECT_EQ("dir1", paths[0]); + EXPECT_EQ("dir2", paths[1]); +} + +// +// Tests for input file order +// + +TEST_F(WinLinkParserTest, InputOrder) { + EXPECT_TRUE(parse("link.exe", "a.lib", "b.obj", "c.obj", "a.lib", "d.obj", + nullptr)); + EXPECT_EQ(5, inputFileCount()); + EXPECT_EQ("b.obj", inputFile(0)); + EXPECT_EQ("c.obj", inputFile(1)); + EXPECT_EQ("d.obj", inputFile(2)); + EXPECT_EQ("a.lib", inputFile(3)); +} + +// +// Tests for command line options that take values. +// + +TEST_F(WinLinkParserTest, AlternateName) { + EXPECT_TRUE(parse("link.exe", "/alternatename:sym1=sym", + "/alternatename:sym2=sym", "a.out", nullptr)); + const std::set<std::string> &aliases = _ctx.getAlternateNames("sym"); + EXPECT_EQ(2U, aliases.size()); + auto it = aliases.begin(); + EXPECT_EQ("sym1", *it++); + EXPECT_EQ("sym2", *it++); +} + +TEST_F(WinLinkParserTest, Export) { + EXPECT_TRUE(parse("link.exe", "/export:foo", "a.out", nullptr)); + const std::vector<PECOFFLinkingContext::ExportDesc> &exports = + _ctx.getDllExports(); + EXPECT_EQ(1U, exports.size()); + EXPECT_EQ("_foo", exports[0].name); + EXPECT_EQ(-1, exports[0].ordinal); + EXPECT_FALSE(exports[0].noname); + EXPECT_FALSE(exports[0].isData); +} + +TEST_F(WinLinkParserTest, ExportWithOptions) { + EXPECT_TRUE(parse("link.exe", "/export:foo,@8,noname,data", + "/export:bar,@10,data", "a.out", nullptr)); + const std::vector<PECOFFLinkingContext::ExportDesc> &exports = + _ctx.getDllExports(); + EXPECT_EQ(2U, exports.size()); + EXPECT_EQ("_foo", exports[0].name); + EXPECT_EQ(8, exports[0].ordinal); + EXPECT_TRUE(exports[0].noname); + EXPECT_TRUE(exports[0].isData); + EXPECT_EQ("_bar", exports[1].name); + EXPECT_EQ(10, exports[1].ordinal); + EXPECT_FALSE(exports[1].noname); + EXPECT_TRUE(exports[1].isData); +} + +TEST_F(WinLinkParserTest, ExportDuplicateExports) { + EXPECT_TRUE( + parse("link.exe", "/export:foo", "/export:foo,@2", "a.out", nullptr)); + const std::vector<PECOFFLinkingContext::ExportDesc> &exports = + _ctx.getDllExports(); + EXPECT_EQ(1U, exports.size()); + EXPECT_EQ("_foo", exports[0].name); + EXPECT_EQ(-1, exports[0].ordinal); +} + +TEST_F(WinLinkParserTest, ExportDuplicateOrdinals) { + EXPECT_FALSE( + parse("link.exe", "/export:foo,@1", "/export:bar,@1", "a.out", nullptr)); +} + +TEST_F(WinLinkParserTest, ExportInvalid1) { + EXPECT_FALSE(parse("link.exe", "/export:foo,@0", "a.out", nullptr)); +} + +TEST_F(WinLinkParserTest, ExportInvalid2) { + EXPECT_FALSE(parse("link.exe", "/export:foo,@65536", "a.out", nullptr)); +} + +TEST_F(WinLinkParserTest, MachineX86) { + EXPECT_TRUE(parse("link.exe", "/machine:x86", "a.obj", nullptr)); + EXPECT_EQ(llvm::COFF::IMAGE_FILE_MACHINE_I386, _ctx.getMachineType()); +} + +TEST_F(WinLinkParserTest, MachineX64) { + EXPECT_TRUE(parse("link.exe", "/machine:x64", "a.obj", nullptr)); + EXPECT_EQ(llvm::COFF::IMAGE_FILE_MACHINE_AMD64, _ctx.getMachineType()); +} + +TEST_F(WinLinkParserTest, MachineArm) { + EXPECT_TRUE(parse("link.exe", "/machine:arm", "a.obj", nullptr)); + EXPECT_EQ(llvm::COFF::IMAGE_FILE_MACHINE_ARMNT, _ctx.getMachineType()); +} + +TEST_F(WinLinkParserTest, MachineUnknown) { + EXPECT_FALSE(parse("link.exe", "/machine:nosucharch", "a.obj", nullptr)); + EXPECT_EQ("error: unknown machine type: nosucharch\n", errorMessage()); +} + +TEST_F(WinLinkParserTest, MajorImageVersion) { + EXPECT_TRUE(parse("link.exe", "/version:7", "foo.o", nullptr)); + EXPECT_EQ(7, _ctx.getImageVersion().majorVersion); + EXPECT_EQ(0, _ctx.getImageVersion().minorVersion); +} + +TEST_F(WinLinkParserTest, MajorMinorImageVersion) { + EXPECT_TRUE(parse("link.exe", "/version:72.35", "foo.o", nullptr)); + EXPECT_EQ(72, _ctx.getImageVersion().majorVersion); + EXPECT_EQ(35, _ctx.getImageVersion().minorVersion); +} + +TEST_F(WinLinkParserTest, MinMajorOSVersion) { + EXPECT_TRUE(parse("link.exe", "/subsystem:windows,3", "foo.o", nullptr)); + EXPECT_EQ(llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_GUI, _ctx.getSubsystem()); + EXPECT_EQ(3, _ctx.getMinOSVersion().majorVersion); + EXPECT_EQ(0, _ctx.getMinOSVersion().minorVersion); +} + +TEST_F(WinLinkParserTest, MinMajorMinorOSVersion) { + EXPECT_TRUE(parse("link.exe", "/subsystem:windows,3.1", "foo.o", nullptr)); + EXPECT_EQ(llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_GUI, _ctx.getSubsystem()); + EXPECT_EQ(3, _ctx.getMinOSVersion().majorVersion); + EXPECT_EQ(1, _ctx.getMinOSVersion().minorVersion); +} + +TEST_F(WinLinkParserTest, Base) { + EXPECT_TRUE(parse("link.exe", "/base:8388608", "a.obj", nullptr)); + EXPECT_EQ(0x800000U, _ctx.getBaseAddress()); +} + +TEST_F(WinLinkParserTest, InvalidBase) { + EXPECT_FALSE(parse("link.exe", "/base:1234", "a.obj", nullptr)); + EXPECT_TRUE(StringRef(errorMessage()) + .startswith("Base address have to be multiple of 64K")); +} + +TEST_F(WinLinkParserTest, StackReserve) { + EXPECT_TRUE(parse("link.exe", "/stack:8192", "a.obj", nullptr)); + EXPECT_EQ(8192U, _ctx.getStackReserve()); + EXPECT_EQ(4096U, _ctx.getStackCommit()); +} + +TEST_F(WinLinkParserTest, StackReserveAndCommit) { + EXPECT_TRUE(parse("link.exe", "/stack:16384,8192", "a.obj", nullptr)); + EXPECT_EQ(16384U, _ctx.getStackReserve()); + EXPECT_EQ(8192U, _ctx.getStackCommit()); +} + +TEST_F(WinLinkParserTest, InvalidStackSize) { + EXPECT_FALSE(parse("link.exe", "/stack:8192,16384", "a.obj", nullptr)); + EXPECT_TRUE(StringRef(errorMessage()).startswith("Invalid stack size")); +} + +TEST_F(WinLinkParserTest, HeapReserve) { + EXPECT_TRUE(parse("link.exe", "/heap:8192", "a.obj", nullptr)); + EXPECT_EQ(8192U, _ctx.getHeapReserve()); + EXPECT_EQ(4096U, _ctx.getHeapCommit()); +} + +TEST_F(WinLinkParserTest, HeapReserveAndCommit) { + EXPECT_TRUE(parse("link.exe", "/heap:16384,8192", "a.obj", nullptr)); + EXPECT_EQ(16384U, _ctx.getHeapReserve()); + EXPECT_EQ(8192U, _ctx.getHeapCommit()); +} + +TEST_F(WinLinkParserTest, InvalidHeapSize) { + EXPECT_FALSE(parse("link.exe", "/heap:8192,16384", "a.obj", nullptr)); + EXPECT_TRUE(StringRef(errorMessage()).startswith("Invalid heap size")); +} + +TEST_F(WinLinkParserTest, SectionAlignment) { + EXPECT_TRUE(parse("link.exe", "/align:8192", "a.obj", nullptr)); + EXPECT_EQ(8192U, _ctx.getSectionDefaultAlignment()); +} + +TEST_F(WinLinkParserTest, InvalidAlignment) { + EXPECT_FALSE(parse("link.exe", "/align:1000", "a.obj", nullptr)); + EXPECT_EQ("Section alignment must be a power of 2, but got 1000\n", + errorMessage()); +} + +TEST_F(WinLinkParserTest, Include) { + EXPECT_TRUE(parse("link.exe", "/include:foo", "a.out", nullptr)); + auto symbols = _ctx.initialUndefinedSymbols(); + EXPECT_FALSE(symbols.empty()); + EXPECT_EQ("foo", symbols[0]); +} + +TEST_F(WinLinkParserTest, Merge) { + EXPECT_TRUE(parse("link.exe", "/merge:.foo=.bar", "/merge:.bar=.baz", + "a.out", nullptr)); + EXPECT_EQ(".baz", _ctx.getOutputSectionName(".foo")); + EXPECT_EQ(".baz", _ctx.getOutputSectionName(".bar")); + EXPECT_EQ(".abc", _ctx.getOutputSectionName(".abc")); +} + +TEST_F(WinLinkParserTest, Merge_Circular) { + EXPECT_FALSE(parse("link.exe", "/merge:.foo=.bar", "/merge:.bar=.foo", + "a.out", nullptr)); +} + +TEST_F(WinLinkParserTest, Implib) { + EXPECT_TRUE(parse("link.exe", "/implib:foo.dll.lib", "a.out", nullptr)); + EXPECT_EQ("foo.dll.lib", _ctx.getOutputImportLibraryPath()); +} + +TEST_F(WinLinkParserTest, ImplibDefault) { + EXPECT_TRUE(parse("link.exe", "/out:foobar.dll", "a.out", nullptr)); + EXPECT_EQ("foobar.lib", _ctx.getOutputImportLibraryPath()); +} + +// +// Tests for /section +// + +namespace { +const uint32_t discardable = llvm::COFF::IMAGE_SCN_MEM_DISCARDABLE; +const uint32_t not_cached = llvm::COFF::IMAGE_SCN_MEM_NOT_CACHED; +const uint32_t not_paged = llvm::COFF::IMAGE_SCN_MEM_NOT_PAGED; +const uint32_t shared = llvm::COFF::IMAGE_SCN_MEM_SHARED; +const uint32_t execute = llvm::COFF::IMAGE_SCN_MEM_EXECUTE; +const uint32_t read = llvm::COFF::IMAGE_SCN_MEM_READ; +const uint32_t write = llvm::COFF::IMAGE_SCN_MEM_WRITE; + +#define TEST_SECTION(testname, arg, expect) \ + TEST_F(WinLinkParserTest, testname) { \ + EXPECT_TRUE(parse("link.exe", "/section:.text," arg, "a.obj", nullptr)); \ + EXPECT_EQ(expect, _ctx.getSectionAttributes(".text", execute | read)); \ + } + +TEST_SECTION(SectionD, "d", execute | read | discardable) +TEST_SECTION(SectionE, "e", execute) +TEST_SECTION(SectionK, "k", execute | read | not_cached) +TEST_SECTION(SectionP, "p", execute | read | not_paged) +TEST_SECTION(SectionR, "r", read) +TEST_SECTION(SectionS, "s", execute | read | shared) +TEST_SECTION(SectionW, "w", write) + +#undef TEST_SECTION + +TEST_F(WinLinkParserTest, Section) { + EXPECT_TRUE(parse("link.exe", "/section:.text,dekprsw", + "/section:.text,!dekprsw", "a.obj", nullptr)); + EXPECT_EQ(0U, _ctx.getSectionAttributes(".text", execute | read)); +} + +TEST_F(WinLinkParserTest, SectionNegate) { + EXPECT_TRUE(parse("link.exe", "/section:.text,!e", "a.obj", nullptr)); + EXPECT_EQ(read, _ctx.getSectionAttributes(".text", execute | read)); +} + +TEST_F(WinLinkParserTest, SectionMultiple) { + EXPECT_TRUE(parse("link.exe", "/section:.foo,e", "/section:.foo,rw", + "/section:.foo,!d", "a.obj", nullptr)); + uint32_t flags = execute | read | not_paged | discardable; + uint32_t expected = execute | read | write | not_paged; + EXPECT_EQ(expected, _ctx.getSectionAttributes(".foo", flags)); +} + +} // end anonymous namespace + +// +// Tests for /defaultlib and /nodefaultlib. +// + +TEST_F(WinLinkParserTest, DefaultLib) { + EXPECT_TRUE(parse("link.exe", "/defaultlib:user32.lib", + "/defaultlib:kernel32", "a.obj", nullptr)); + EXPECT_EQ(4, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); + EXPECT_EQ("user32.lib", inputFile(1)); + EXPECT_EQ("kernel32.lib", inputFile(2)); +} + +TEST_F(WinLinkParserTest, DefaultLibDuplicates) { + EXPECT_TRUE(parse("link.exe", "/defaultlib:user32.lib", + "/defaultlib:user32.lib", "a.obj", nullptr)); + EXPECT_EQ(3, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); + EXPECT_EQ("user32.lib", inputFile(1)); +} + +TEST_F(WinLinkParserTest, NoDefaultLib) { + EXPECT_TRUE(parse("link.exe", "/defaultlib:user32.lib", + "/defaultlib:kernel32", "/nodefaultlib:user32.lib", "a.obj", + nullptr)); + EXPECT_EQ(3, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); + EXPECT_EQ("kernel32.lib", inputFile(1)); +} + +TEST_F(WinLinkParserTest, NoDefaultLibCase) { + EXPECT_TRUE(parse("link.exe", "/defaultlib:user32", + "/defaultlib:kernel32", "/nodefaultlib:USER32.LIB", "a.obj", + nullptr)); + EXPECT_EQ(3, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); + EXPECT_EQ("kernel32.lib", inputFile(1)); +} + +TEST_F(WinLinkParserTest, NoDefaultLibAll) { + EXPECT_TRUE(parse("link.exe", "/defaultlib:user32.lib", + "/defaultlib:kernel32", "/nodefaultlib", "a.obj", nullptr)); + EXPECT_EQ(2, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); +} + +TEST_F(WinLinkParserTest, DisallowLib) { + EXPECT_TRUE(parse("link.exe", "/defaultlib:user32.lib", + "/defaultlib:kernel32", "/disallowlib:user32.lib", "a.obj", + nullptr)); + EXPECT_EQ(3, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); + EXPECT_EQ("kernel32.lib", inputFile(1)); +} + +// +// Tests for DLL. +// + +TEST_F(WinLinkParserTest, NoEntry) { + EXPECT_TRUE(parse("link.exe", "/noentry", "/dll", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.isDll()); + EXPECT_EQ(0x10000000U, _ctx.getBaseAddress()); + EXPECT_EQ("", _ctx.entrySymbolName()); +} + +TEST_F(WinLinkParserTest, NoEntryError) { + // /noentry without /dll is an error. + EXPECT_FALSE(parse("link.exe", "/noentry", "a.obj", nullptr)); + EXPECT_EQ("/noentry must be specified with /dll\n", errorMessage()); +} + +// +// Tests for DELAYLOAD. +// + +TEST_F(WinLinkParserTest, DelayLoad) { + EXPECT_TRUE(parse("link.exe", "/delayload:abc.dll", "/delayload:def.dll", + "a.obj", nullptr)); + EXPECT_TRUE(_ctx.isDelayLoadDLL("abc.dll")); + EXPECT_TRUE(_ctx.isDelayLoadDLL("DEF.DLL")); + EXPECT_FALSE(_ctx.isDelayLoadDLL("xyz.dll")); +} + +// +// Tests for SEH. +// + +TEST_F(WinLinkParserTest, SafeSEH) { + EXPECT_TRUE(parse("link.exe", "/safeseh", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.requireSEH()); + EXPECT_FALSE(_ctx.noSEH()); +} + +TEST_F(WinLinkParserTest, NoSafeSEH) { + EXPECT_TRUE(parse("link.exe", "/safeseh:no", "a.obj", nullptr)); + EXPECT_FALSE(_ctx.requireSEH()); + EXPECT_TRUE(_ctx.noSEH()); +} + +// +// Tests for boolean flags. +// + +TEST_F(WinLinkParserTest, Force) { + EXPECT_TRUE(parse("link.exe", "/force", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.allowRemainingUndefines()); +} + +TEST_F(WinLinkParserTest, ForceUnresolved) { + EXPECT_TRUE(parse("link.exe", "/force:unresolved", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.allowRemainingUndefines()); +} + +TEST_F(WinLinkParserTest, NoNxCompat) { + EXPECT_TRUE(parse("link.exe", "/nxcompat:no", "a.obj", nullptr)); + EXPECT_FALSE(_ctx.isNxCompat()); +} + +TEST_F(WinLinkParserTest, LargeAddressAware) { + EXPECT_TRUE(parse("link.exe", "/largeaddressaware", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.getLargeAddressAware()); +} + +TEST_F(WinLinkParserTest, NoLargeAddressAware) { + EXPECT_TRUE(parse("link.exe", "/largeaddressaware:no", "a.obj", nullptr)); + EXPECT_FALSE(_ctx.getLargeAddressAware()); +} + +TEST_F(WinLinkParserTest, AllowBind) { + EXPECT_TRUE(parse("link.exe", "/allowbind", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.getAllowBind()); +} + +TEST_F(WinLinkParserTest, NoAllowBind) { + EXPECT_TRUE(parse("link.exe", "/allowbind:no", "a.obj", nullptr)); + EXPECT_FALSE(_ctx.getAllowBind()); +} + +TEST_F(WinLinkParserTest, AllowIsolation) { + EXPECT_TRUE(parse("link.exe", "/allowisolation", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.getAllowIsolation()); +} + +TEST_F(WinLinkParserTest, NoAllowIsolation) { + EXPECT_TRUE(parse("link.exe", "/allowisolation:no", "a.obj", nullptr)); + EXPECT_FALSE(_ctx.getAllowIsolation()); +} + +TEST_F(WinLinkParserTest, SwapRunFromCD) { + EXPECT_TRUE(parse("link.exe", "/swaprun:cd", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.getSwapRunFromCD()); +} + +TEST_F(WinLinkParserTest, SwapRunFromNet) { + EXPECT_TRUE(parse("link.exe", "/swaprun:net", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.getSwapRunFromNet()); +} + +TEST_F(WinLinkParserTest, Debug) { + EXPECT_TRUE(parse("link.exe", "/debug", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.deadStrip()); + EXPECT_TRUE(_ctx.getDebug()); + EXPECT_EQ("a.pdb", _ctx.getPDBFilePath()); +} + +TEST_F(WinLinkParserTest, PDB) { + EXPECT_TRUE(parse("link.exe", "/debug", "/pdb:foo.pdb", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.getDebug()); + EXPECT_EQ("foo.pdb", _ctx.getPDBFilePath()); +} + +TEST_F(WinLinkParserTest, Fixed) { + EXPECT_TRUE(parse("link.exe", "/fixed", "a.out", nullptr)); + EXPECT_FALSE(_ctx.getBaseRelocationEnabled()); + EXPECT_FALSE(_ctx.getDynamicBaseEnabled()); +} + +TEST_F(WinLinkParserTest, NoFixed) { + EXPECT_TRUE(parse("link.exe", "/fixed:no", "a.out", nullptr)); + EXPECT_TRUE(_ctx.getBaseRelocationEnabled()); +} + +TEST_F(WinLinkParserTest, TerminalServerAware) { + EXPECT_TRUE(parse("link.exe", "/tsaware", "a.out", nullptr)); + EXPECT_TRUE(_ctx.isTerminalServerAware()); +} + +TEST_F(WinLinkParserTest, NoTerminalServerAware) { + EXPECT_TRUE(parse("link.exe", "/tsaware:no", "a.out", nullptr)); + EXPECT_FALSE(_ctx.isTerminalServerAware()); +} + +TEST_F(WinLinkParserTest, DynamicBase) { + EXPECT_TRUE(parse("link.exe", "/dynamicbase", "a.out", nullptr)); + EXPECT_TRUE(_ctx.getDynamicBaseEnabled()); +} + +TEST_F(WinLinkParserTest, NoDynamicBase) { + EXPECT_TRUE(parse("link.exe", "/dynamicbase:no", "a.out", nullptr)); + EXPECT_FALSE(_ctx.getDynamicBaseEnabled()); +} + +// +// Test for /failifmismatch +// + +TEST_F(WinLinkParserTest, FailIfMismatch_Match) { + EXPECT_TRUE(parse("link.exe", "/failifmismatch:foo=bar", + "/failifmismatch:foo=bar", "/failifmismatch:abc=def", + "a.out", nullptr)); +} + +TEST_F(WinLinkParserTest, FailIfMismatch_Mismatch) { + EXPECT_FALSE(parse("link.exe", "/failifmismatch:foo=bar", + "/failifmismatch:foo=baz", "a.out", nullptr)); +} + +// +// Tests for /manifest, /manifestuac, /manifestfile, and /manifestdependency. +// +TEST_F(WinLinkParserTest, Manifest_Default) { + EXPECT_TRUE(parse("link.exe", "/manifest", "a.out", nullptr)); + EXPECT_TRUE(_ctx.getCreateManifest()); + EXPECT_FALSE(_ctx.getEmbedManifest()); + EXPECT_EQ(1, _ctx.getManifestId()); + EXPECT_EQ("'asInvoker'", _ctx.getManifestLevel()); + EXPECT_EQ("'false'", _ctx.getManifestUiAccess()); +} + +TEST_F(WinLinkParserTest, Manifest_No) { + EXPECT_TRUE(parse("link.exe", "/manifest:no", "a.out", nullptr)); + EXPECT_FALSE(_ctx.getCreateManifest()); +} + +TEST_F(WinLinkParserTest, Manifestuac_no) { + EXPECT_TRUE(parse("link.exe", "/manifestuac:NO", "a.out", nullptr)); + EXPECT_FALSE(_ctx.getManifestUAC()); +} + +TEST_F(WinLinkParserTest, Manifestuac_Level) { + EXPECT_TRUE(parse("link.exe", "/manifestuac:level='requireAdministrator'", + "a.out", nullptr)); + EXPECT_EQ("'requireAdministrator'", _ctx.getManifestLevel()); + EXPECT_EQ("'false'", _ctx.getManifestUiAccess()); +} + +TEST_F(WinLinkParserTest, Manifestuac_UiAccess) { + EXPECT_TRUE(parse("link.exe", "/manifestuac:uiAccess='true'", "a.out", nullptr)); + EXPECT_EQ("'asInvoker'", _ctx.getManifestLevel()); + EXPECT_EQ("'true'", _ctx.getManifestUiAccess()); +} + +TEST_F(WinLinkParserTest, Manifestuac_LevelAndUiAccess) { + EXPECT_TRUE(parse("link.exe", + "/manifestuac:level='requireAdministrator' uiAccess='true'", + "a.out", nullptr)); + EXPECT_EQ("'requireAdministrator'", _ctx.getManifestLevel()); + EXPECT_EQ("'true'", _ctx.getManifestUiAccess()); +} + +TEST_F(WinLinkParserTest, Manifestfile) { + EXPECT_TRUE(parse("link.exe", "/manifestfile:bar.manifest", + "a.out", nullptr)); + EXPECT_EQ("bar.manifest", _ctx.getManifestOutputPath()); +} + +TEST_F(WinLinkParserTest, Manifestdependency) { + EXPECT_TRUE(parse("link.exe", "/manifestdependency:foo bar", "a.out", + nullptr)); + EXPECT_EQ("foo bar", _ctx.getManifestDependency()); +} + +// +// Test for /OPT +// + +TEST_F(WinLinkParserTest, OptNoRef) { + EXPECT_TRUE(parse("link.exe", "/opt:noref", "a.obj", nullptr)); + EXPECT_FALSE(_ctx.deadStrip()); +} + +TEST_F(WinLinkParserTest, OptIgnore) { + EXPECT_TRUE(parse("link.exe", "/opt:ref", "/opt:icf", "/opt:noicf", + "/opt:icf=foo", "/opt:lbr", "/opt:nolbr", "a.obj", + nullptr)); +} + +TEST_F(WinLinkParserTest, OptUnknown) { + EXPECT_FALSE(parse("link.exe", "/opt:foo", "a.obj", nullptr)); +} + +// +// Test for /PROFILE +// + +TEST_F(WinLinkParserTest, Profile) { + EXPECT_TRUE(parse("link.exe", "/profile", "a.obj", nullptr)); + EXPECT_TRUE(_ctx.deadStrip()); + EXPECT_TRUE(_ctx.getBaseRelocationEnabled()); + EXPECT_TRUE(_ctx.getDynamicBaseEnabled()); +} + +// +// Test for command line flags that are ignored. +// + +TEST_F(WinLinkParserTest, Ignore) { + // There are some no-op command line options that are recognized for + // compatibility with link.exe. + EXPECT_TRUE(parse("link.exe", "/nologo", "/errorreport:prompt", + "/incremental", "/incremental:no", "/delay:unload", + "/disallowlib:foo", "/pdbaltpath:bar", + "/wx", "/wx:no", "/tlbid:1", "/tlbout:foo", "/idlout:foo", + "/ignore:4000", "/ignoreidl", "/implib:foo", "/safeseh", + "/safeseh:no", "/functionpadmin", "/maxilksize:1024", + "a.obj", nullptr)); + EXPECT_EQ("", errorMessage()); + EXPECT_EQ(2, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); +} + +// +// Test for "--" +// + +TEST_F(WinLinkParserTest, DashDash) { + EXPECT_TRUE(parse("link.exe", "/subsystem:console", "/out:a.exe", "a.obj", + "--", "b.obj", "-c.obj", nullptr)); + EXPECT_EQ(llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI, _ctx.getSubsystem()); + EXPECT_EQ("a.exe", _ctx.outputPath()); + EXPECT_EQ(4, inputFileCount()); + EXPECT_EQ("a.obj", inputFile(0)); + EXPECT_EQ("b.obj", inputFile(1)); + EXPECT_EQ("-c.obj", inputFile(2)); +} diff --git a/unittests/DriverTests/WinLinkModuleDefTest.cpp b/unittests/DriverTests/WinLinkModuleDefTest.cpp new file mode 100644 index 0000000000000..6762fd4b2ea66 --- /dev/null +++ b/unittests/DriverTests/WinLinkModuleDefTest.cpp @@ -0,0 +1,155 @@ +//===- lld/unittest/WinLinkModuleDefTest.cpp ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "lld/Driver/WinLinkModuleDef.h" +#include "llvm/Support/MemoryBuffer.h" +#include <memory> + +using namespace llvm; +using namespace lld; + +class ParserTest : public testing::Test { +protected: + std::vector<moduledef::Directive *> _dirs; + + void parse(const char *contents) { + auto membuf = + std::unique_ptr<MemoryBuffer>(MemoryBuffer::getMemBuffer(contents)); + moduledef::Lexer lexer(std::move(membuf)); + moduledef::Parser parser(lexer, _alloc); + EXPECT_TRUE(parser.parse(_dirs)); + EXPECT_TRUE(!_dirs.empty()); + } + + void verifyExportDesc(const PECOFFLinkingContext::ExportDesc &exp, + StringRef sym, int ordinal, bool noname, bool isData) { + EXPECT_EQ(sym, exp.name); + EXPECT_EQ(ordinal, exp.ordinal); + EXPECT_EQ(noname, exp.noname); + EXPECT_EQ(isData, exp.isData); + } + +private: + llvm::BumpPtrAllocator _alloc; +}; + +TEST_F(ParserTest, Exports) { + parse("EXPORTS\n" + " sym1\n" + " sym2 @5\n" + " sym3 @8 NONAME\n" + " sym4 DATA\n" + " sym5 @10 NONAME DATA\n"); + EXPECT_EQ(1U, _dirs.size()); + const std::vector<PECOFFLinkingContext::ExportDesc> &exports = + cast<moduledef::Exports>(_dirs[0])->getExports(); + EXPECT_EQ(5U, exports.size()); + verifyExportDesc(exports[0], "sym1", -1, false, false); + verifyExportDesc(exports[1], "sym2", 5, false, false); + verifyExportDesc(exports[2], "sym3", 8, true, false); + verifyExportDesc(exports[3], "sym4", -1, false, true); + verifyExportDesc(exports[4], "sym5", 10, true, true); +} + +TEST_F(ParserTest, Heapsize) { + parse("HEAPSIZE 65536"); + EXPECT_EQ(1U, _dirs.size()); + auto *heapsize = cast<moduledef::Heapsize>(_dirs[0]); + EXPECT_EQ(65536U, heapsize->getReserve()); + EXPECT_EQ(0U, heapsize->getCommit()); +} + +TEST_F(ParserTest, HeapsizeWithCommit) { + parse("HEAPSIZE 65536, 8192"); + EXPECT_EQ(1U, _dirs.size()); + auto *heapsize = cast<moduledef::Heapsize>(_dirs[0]); + EXPECT_EQ(65536U, heapsize->getReserve()); + EXPECT_EQ(8192U, heapsize->getCommit()); +} + +TEST_F(ParserTest, StacksizeBasic) { + parse("STACKSIZE 65536"); + EXPECT_EQ(1U, _dirs.size()); + auto *stacksize = cast<moduledef::Stacksize>(_dirs[0]); + EXPECT_EQ(65536U, stacksize->getReserve()); + EXPECT_EQ(0U, stacksize->getCommit()); +} + +TEST_F(ParserTest, StacksizeWithCommit) { + parse("STACKSIZE 65536, 8192"); + EXPECT_EQ(1U, _dirs.size()); + auto *stacksize = cast<moduledef::Stacksize>(_dirs[0]); + EXPECT_EQ(65536U, stacksize->getReserve()); + EXPECT_EQ(8192U, stacksize->getCommit()); +} + +TEST_F(ParserTest, Library) { + parse("LIBRARY foo.dll"); + EXPECT_EQ(1U, _dirs.size()); + auto *lib = cast<moduledef::Library>(_dirs[0]); + EXPECT_EQ("foo.dll", lib->getName()); +} + +TEST_F(ParserTest, NameBasic) { + parse("NAME foo.exe"); + EXPECT_EQ(1U, _dirs.size()); + auto *name = cast<moduledef::Name>(_dirs[0]); + EXPECT_EQ("foo.exe", name->getOutputPath()); + EXPECT_EQ(0U, name->getBaseAddress()); +} + +TEST_F(ParserTest, NameWithBase) { + parse("NAME foo.exe BASE=4096"); + EXPECT_EQ(1U, _dirs.size()); + auto *name = cast<moduledef::Name>(_dirs[0]); + EXPECT_EQ("foo.exe", name->getOutputPath()); + EXPECT_EQ(4096U, name->getBaseAddress()); +} + +TEST_F(ParserTest, NameLongFileName) { + parse("NAME \"a long file name.exe\""); + EXPECT_EQ(1U, _dirs.size()); + auto *name = cast<moduledef::Name>(_dirs[0]); + EXPECT_EQ("a long file name.exe", name->getOutputPath()); + EXPECT_EQ(0U, name->getBaseAddress()); +} + +TEST_F(ParserTest, VersionMajor) { + parse("VERSION 12"); + EXPECT_EQ(1U, _dirs.size()); + auto *ver = cast<moduledef::Version>(_dirs[0]); + EXPECT_EQ(12, ver->getMajorVersion()); + EXPECT_EQ(0, ver->getMinorVersion()); +} + +TEST_F(ParserTest, VersionMajorMinor) { + parse("VERSION 12.34"); + EXPECT_EQ(1U, _dirs.size()); + auto *ver = cast<moduledef::Version>(_dirs[0]); + EXPECT_EQ(12, ver->getMajorVersion()); + EXPECT_EQ(34, ver->getMinorVersion()); +} + +TEST_F(ParserTest, Multiple) { + parse("LIBRARY foo\n" + "EXPORTS sym\n" + "VERSION 12"); + EXPECT_EQ(3U, _dirs.size()); + auto *lib = cast<moduledef::Library>(_dirs[0]); + EXPECT_EQ("foo.dll", lib->getName()); + + const std::vector<PECOFFLinkingContext::ExportDesc> &exports = + cast<moduledef::Exports>(_dirs[1])->getExports(); + EXPECT_EQ(1U, exports.size()); + verifyExportDesc(exports[0], "sym", -1, false, false); + + auto *ver = cast<moduledef::Version>(_dirs[2]); + EXPECT_EQ(12, ver->getMajorVersion()); +} diff --git a/unittests/MachOTests/CMakeLists.txt b/unittests/MachOTests/CMakeLists.txt new file mode 100644 index 0000000000000..1a683484b5f9d --- /dev/null +++ b/unittests/MachOTests/CMakeLists.txt @@ -0,0 +1,13 @@ + +add_lld_unittest(lldMachOTests + MachONormalizedFileBinaryReaderTests.cpp + MachONormalizedFileBinaryWriterTests.cpp + MachONormalizedFileToAtomsTests.cpp + MachONormalizedFileYAMLTests.cpp + ) + +target_link_libraries(lldMachOTests + lldDriver + lldMachO + lldYAML + ) diff --git a/unittests/MachOTests/MachONormalizedFileBinaryReaderTests.cpp b/unittests/MachOTests/MachONormalizedFileBinaryReaderTests.cpp new file mode 100644 index 0000000000000..22ed3f15b3e6c --- /dev/null +++ b/unittests/MachOTests/MachONormalizedFileBinaryReaderTests.cpp @@ -0,0 +1,748 @@ +//===- lld/unittest/MachOTests/MachONormalizedFileBinaryReaderTests.cpp ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "../../lib/ReaderWriter/MachO/MachONormalizedFile.h" +#include "llvm/Support/MachO.h" +#include <assert.h> +#include <vector> + +using llvm::StringRef; +using llvm::MemoryBuffer; +using llvm::ErrorOr; + +using namespace lld::mach_o::normalized; +using namespace llvm::MachO; + +static std::unique_ptr<NormalizedFile> +fromBinary(const uint8_t bytes[], unsigned length, StringRef archStr) { + StringRef sr((const char*)bytes, length); + std::unique_ptr<MemoryBuffer> mb(MemoryBuffer::getMemBuffer(sr, "", false)); + ErrorOr<std::unique_ptr<NormalizedFile>> r = + lld::mach_o::normalized::readBinary( + mb, lld::MachOLinkingContext::archFromName(archStr)); + EXPECT_FALSE(!r); + return std::move(*r); +} + +// The Mach-O object reader uses functions such as read32 or read64 +// which don't allow unaligned access. Our in-memory object file +// needs to be aligned to a larger boundary than uint8_t's. +#if _MSC_VER +#define FILEBYTES __declspec(align(64)) const uint8_t fileBytes[] +#else +#define FILEBYTES const uint8_t fileBytes[] __attribute__((aligned(64))) +#endif + +TEST(BinaryReaderTest, empty_obj_x86_64) { + FILEBYTES = { + 0xcf, 0xfa, 0xed, 0xfe, 0x07, 0x00, 0x00, 0x01, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + std::unique_ptr<NormalizedFile> f = + fromBinary(fileBytes, sizeof(fileBytes), "x86_64"); + EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64); + EXPECT_EQ((int)(f->fileType), MH_OBJECT); + EXPECT_EQ((int)(f->flags), MH_SUBSECTIONS_VIA_SYMBOLS); + EXPECT_TRUE(f->localSymbols.empty()); + EXPECT_TRUE(f->globalSymbols.empty()); + EXPECT_TRUE(f->undefinedSymbols.empty()); +} + + +TEST(BinaryReaderTest, empty_obj_x86) { + FILEBYTES = { + 0xce, 0xfa, 0xed, 0xfe, 0x07, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x74, 0x65, + 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, + 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + std::unique_ptr<NormalizedFile> f = + fromBinary(fileBytes, sizeof(fileBytes), "i386"); + EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86); + EXPECT_EQ((int)(f->fileType), MH_OBJECT); + EXPECT_EQ((int)(f->flags), MH_SUBSECTIONS_VIA_SYMBOLS); + EXPECT_TRUE(f->localSymbols.empty()); + EXPECT_TRUE(f->globalSymbols.empty()); + EXPECT_TRUE(f->undefinedSymbols.empty()); +} + + +TEST(BinaryReaderTest, empty_obj_ppc) { + FILEBYTES = { + 0xfe, 0xed, 0xfa, 0xce, 0x00, 0x00, 0x00, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7c, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x74, 0x65, + 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, + 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + std::unique_ptr<NormalizedFile> f = + fromBinary(fileBytes, sizeof(fileBytes), "ppc"); + EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_ppc); + EXPECT_EQ((int)(f->fileType), MH_OBJECT); + EXPECT_EQ((int)(f->flags), MH_SUBSECTIONS_VIA_SYMBOLS); + EXPECT_TRUE(f->localSymbols.empty()); + EXPECT_TRUE(f->globalSymbols.empty()); + EXPECT_TRUE(f->undefinedSymbols.empty()); +} + + +TEST(BinaryReaderTest, empty_obj_armv7) { + FILEBYTES = { + 0xce, 0xfa, 0xed, 0xfe, 0x0c, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x74, 0x65, + 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, + 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + std::unique_ptr<NormalizedFile> f = + fromBinary(fileBytes, sizeof(fileBytes), "armv7"); + EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv7); + EXPECT_EQ((int)(f->fileType), MH_OBJECT); + EXPECT_EQ((int)(f->flags), MH_SUBSECTIONS_VIA_SYMBOLS); + EXPECT_TRUE(f->localSymbols.empty()); + EXPECT_TRUE(f->globalSymbols.empty()); + EXPECT_TRUE(f->undefinedSymbols.empty()); +} + +TEST(BinaryReaderTest, empty_obj_x86_64_arm7) { + FILEBYTES = { +#include "empty_obj_x86_armv7.txt" + }; + std::unique_ptr<NormalizedFile> f = + fromBinary(fileBytes, sizeof(fileBytes), "x86_64"); + EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64); + EXPECT_EQ((int)(f->fileType), MH_OBJECT); + EXPECT_EQ((int)(f->flags), MH_SUBSECTIONS_VIA_SYMBOLS); + EXPECT_TRUE(f->localSymbols.empty()); + EXPECT_TRUE(f->globalSymbols.empty()); + EXPECT_TRUE(f->undefinedSymbols.empty()); + + std::unique_ptr<NormalizedFile> f2 = + fromBinary(fileBytes, sizeof(fileBytes), "armv7"); + EXPECT_EQ(f2->arch, lld::MachOLinkingContext::arch_armv7); + EXPECT_EQ((int)(f2->fileType), MH_OBJECT); + EXPECT_EQ((int)(f2->flags), MH_SUBSECTIONS_VIA_SYMBOLS); + EXPECT_TRUE(f2->localSymbols.empty()); + EXPECT_TRUE(f2->globalSymbols.empty()); + EXPECT_TRUE(f2->undefinedSymbols.empty()); +} + +TEST(BinaryReaderTest, hello_obj_x86_64) { + FILEBYTES = { + 0xCF, 0xFA, 0xED, 0xFE, 0x07, 0x00, 0x00, 0x01, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5F, 0x5F, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5F, 0x5F, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xA4, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5F, 0x5F, 0x63, 0x73, 0x74, 0x72, 0x69, 0x6E, + 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5F, 0x5F, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0xB4, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0xE4, 0x01, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x55, 0x48, 0x89, 0xE5, 0x48, 0x83, 0xEC, 0x10, + 0x48, 0x8D, 0x3D, 0x00, 0x00, 0x00, 0x00, 0xC7, + 0x45, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xB0, 0x00, + 0xE8, 0x00, 0x00, 0x00, 0x00, 0xB9, 0x00, 0x00, + 0x00, 0x00, 0x89, 0x45, 0xF8, 0x89, 0xC8, 0x48, + 0x83, 0xC4, 0x10, 0x5D, 0xC3, 0x68, 0x65, 0x6C, + 0x6C, 0x6F, 0x0A, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x2D, 0x0B, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1D, 0x0F, 0x00, 0x00, 0x00, + 0x0E, 0x02, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x6D, 0x61, + 0x69, 0x6E, 0x00, 0x5F, 0x70, 0x72, 0x69, 0x6E, + 0x74, 0x66, 0x00, 0x4C, 0x5F, 0x2E, 0x73, 0x74, + 0x72, 0x00, 0x00, 0x00 }; + std::unique_ptr<NormalizedFile> f = + fromBinary(fileBytes, sizeof(fileBytes), "x86_64"); + + EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64); + EXPECT_EQ((int)(f->fileType), MH_OBJECT); + EXPECT_EQ((int)(f->flags), MH_SUBSECTIONS_VIA_SYMBOLS); + EXPECT_EQ(f->sections.size(), 2UL); + const Section& text = f->sections[0]; + EXPECT_TRUE(text.segmentName.equals("__TEXT")); + EXPECT_TRUE(text.sectionName.equals("__text")); + EXPECT_EQ(text.type, S_REGULAR); + EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS + | S_ATTR_SOME_INSTRUCTIONS)); + EXPECT_EQ(text.alignment, 4U); + EXPECT_EQ(text.address, Hex64(0x0)); + EXPECT_EQ(text.content.size(), 45UL); + EXPECT_EQ((int)(text.content[0]), 0x55); + EXPECT_EQ((int)(text.content[1]), 0x48); + EXPECT_TRUE(text.indirectSymbols.empty()); + EXPECT_EQ(text.relocations.size(), 2UL); + const Relocation& call = text.relocations[0]; + EXPECT_EQ(call.offset, Hex32(0x19)); + EXPECT_EQ(call.type, X86_64_RELOC_BRANCH); + EXPECT_EQ(call.length, 2); + EXPECT_EQ(call.isExtern, true); + EXPECT_EQ(call.symbol, 2U); + const Relocation& str = text.relocations[1]; + EXPECT_EQ(str.offset, Hex32(0xB)); + EXPECT_EQ(str.type, X86_64_RELOC_SIGNED); + EXPECT_EQ(str.length, 2); + EXPECT_EQ(str.isExtern, true); + EXPECT_EQ(str.symbol, 0U); + + const Section& cstring = f->sections[1]; + EXPECT_TRUE(cstring.segmentName.equals("__TEXT")); + EXPECT_TRUE(cstring.sectionName.equals("__cstring")); + EXPECT_EQ(cstring.type, S_CSTRING_LITERALS); + EXPECT_EQ(cstring.attributes, SectionAttr(0)); + EXPECT_EQ(cstring.alignment, 0U); + EXPECT_EQ(cstring.address, Hex64(0x02D)); + EXPECT_EQ(cstring.content.size(), 7UL); + EXPECT_EQ((int)(cstring.content[0]), 0x68); + EXPECT_EQ((int)(cstring.content[1]), 0x65); + EXPECT_EQ((int)(cstring.content[2]), 0x6c); + EXPECT_TRUE(cstring.indirectSymbols.empty()); + EXPECT_TRUE(cstring.relocations.empty()); + + EXPECT_EQ(f->localSymbols.size(), 1UL); + const Symbol& strLabel = f->localSymbols[0]; + EXPECT_EQ(strLabel.type, N_SECT); + EXPECT_EQ(strLabel.sect, 2); + EXPECT_EQ(strLabel.value, Hex64(0x2D)); + EXPECT_EQ(f->globalSymbols.size(), 1UL); + const Symbol& mainLabel = f->globalSymbols[0]; + EXPECT_TRUE(mainLabel.name.equals("_main")); + EXPECT_EQ(mainLabel.type, N_SECT); + EXPECT_EQ(mainLabel.sect, 1); + EXPECT_EQ(mainLabel.scope, SymbolScope(N_EXT)); + EXPECT_EQ(mainLabel.value, Hex64(0x0)); + EXPECT_EQ(f->undefinedSymbols.size(), 1UL); + const Symbol& printfLabel = f->undefinedSymbols[0]; + EXPECT_TRUE(printfLabel.name.equals("_printf")); + EXPECT_EQ(printfLabel.type, N_UNDF); + EXPECT_EQ(printfLabel.scope, SymbolScope(N_EXT)); +} + + +TEST(BinaryReaderTest, hello_obj_x86) { + FILEBYTES = { + 0xCE, 0xFA, 0xED, 0xFE, 0x07, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x28, 0x01, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5F, 0x5F, 0x74, 0x65, + 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5F, 0x5F, 0x54, 0x45, + 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x7C, 0x01, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5F, 0x5F, 0x63, 0x73, 0x74, 0x72, 0x69, 0x6E, + 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5F, 0x5F, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x74, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xAC, 0x01, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x55, 0x89, 0xE5, 0x83, + 0xEC, 0x18, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x58, + 0x8D, 0x80, 0x25, 0x00, 0x00, 0x00, 0xC7, 0x45, + 0xFC, 0x00, 0x00, 0x00, 0x00, 0x89, 0x04, 0x24, + 0xE8, 0xDF, 0xFF, 0xFF, 0xFF, 0xB9, 0x00, 0x00, + 0x00, 0x00, 0x89, 0x45, 0xF8, 0x89, 0xC8, 0x83, + 0xC4, 0x18, 0x5D, 0xC3, 0x68, 0x65, 0x6C, 0x6C, + 0x6F, 0x0A, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x0D, 0x0E, 0x00, 0x00, 0xA4, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA1, + 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x6D, 0x61, + 0x69, 0x6E, 0x00, 0x5F, 0x70, 0x72, 0x69, 0x6E, + 0x74, 0x66, 0x00, 0x00 + }; + std::unique_ptr<NormalizedFile> f = + fromBinary(fileBytes, sizeof(fileBytes), "i386"); + + EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86); + EXPECT_EQ((int)(f->fileType), MH_OBJECT); + EXPECT_EQ((int)(f->flags), MH_SUBSECTIONS_VIA_SYMBOLS); + EXPECT_EQ(f->sections.size(), 2UL); + const Section& text = f->sections[0]; + EXPECT_TRUE(text.segmentName.equals("__TEXT")); + EXPECT_TRUE(text.sectionName.equals("__text")); + EXPECT_EQ(text.type, S_REGULAR); + EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS + | S_ATTR_SOME_INSTRUCTIONS)); + EXPECT_EQ(text.alignment, 4U); + EXPECT_EQ(text.address, Hex64(0x0)); + EXPECT_EQ(text.content.size(), 48UL); + EXPECT_EQ((int)(text.content[0]), 0x55); + EXPECT_EQ((int)(text.content[1]), 0x89); + EXPECT_TRUE(text.indirectSymbols.empty()); + EXPECT_EQ(text.relocations.size(), 3UL); + const Relocation& call = text.relocations[0]; + EXPECT_EQ(call.offset, Hex32(0x1D)); + EXPECT_EQ(call.scattered, false); + EXPECT_EQ(call.type, GENERIC_RELOC_VANILLA); + EXPECT_EQ(call.pcRel, true); + EXPECT_EQ(call.length, 2); + EXPECT_EQ(call.isExtern, true); + EXPECT_EQ(call.symbol, 1U); + const Relocation& sectDiff = text.relocations[1]; + EXPECT_EQ(sectDiff.offset, Hex32(0xE)); + EXPECT_EQ(sectDiff.scattered, true); + EXPECT_EQ(sectDiff.type, GENERIC_RELOC_LOCAL_SECTDIFF); + EXPECT_EQ(sectDiff.pcRel, false); + EXPECT_EQ(sectDiff.length, 2); + EXPECT_EQ(sectDiff.value, 0x30U); + const Relocation& pair = text.relocations[2]; + EXPECT_EQ(pair.offset, Hex32(0x0)); + EXPECT_EQ(pair.scattered, true); + EXPECT_EQ(pair.type, GENERIC_RELOC_PAIR); + EXPECT_EQ(pair.pcRel, false); + EXPECT_EQ(pair.length, 2); + EXPECT_EQ(pair.value, 0x0BU); + + const Section& cstring = f->sections[1]; + EXPECT_TRUE(cstring.segmentName.equals("__TEXT")); + EXPECT_TRUE(cstring.sectionName.equals("__cstring")); + EXPECT_EQ(cstring.type, S_CSTRING_LITERALS); + EXPECT_EQ(cstring.attributes, SectionAttr(0)); + EXPECT_EQ(cstring.alignment, 0U); + EXPECT_EQ(cstring.address, Hex64(0x030)); + EXPECT_EQ(cstring.content.size(), 7UL); + EXPECT_EQ((int)(cstring.content[0]), 0x68); + EXPECT_EQ((int)(cstring.content[1]), 0x65); + EXPECT_EQ((int)(cstring.content[2]), 0x6c); + EXPECT_TRUE(cstring.indirectSymbols.empty()); + EXPECT_TRUE(cstring.relocations.empty()); + + EXPECT_EQ(f->localSymbols.size(), 0UL); + EXPECT_EQ(f->globalSymbols.size(), 1UL); + const Symbol& mainLabel = f->globalSymbols[0]; + EXPECT_TRUE(mainLabel.name.equals("_main")); + EXPECT_EQ(mainLabel.type, N_SECT); + EXPECT_EQ(mainLabel.sect, 1); + EXPECT_EQ(mainLabel.scope, SymbolScope(N_EXT)); + EXPECT_EQ(mainLabel.value, Hex64(0x0)); + EXPECT_EQ(f->undefinedSymbols.size(), 1UL); + const Symbol& printfLabel = f->undefinedSymbols[0]; + EXPECT_TRUE(printfLabel.name.equals("_printf")); + EXPECT_EQ(printfLabel.type, N_UNDF); + EXPECT_EQ(printfLabel.scope, SymbolScope(N_EXT)); +} + + +TEST(BinaryReaderTest, hello_obj_armv7) { + FILEBYTES = { + 0xCE, 0xFA, 0xED, 0xFE, 0x0C, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x28, 0x01, 0x00, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x31, 0x00, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, + 0x31, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5F, 0x5F, 0x74, 0x65, + 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5F, 0x5F, 0x54, 0x45, + 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5F, 0x5F, 0x63, 0x73, 0x74, 0x72, 0x69, 0x6E, + 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5F, 0x5F, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2A, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x6E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0xA0, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xB8, 0x01, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xB5, 0x6F, 0x46, + 0x82, 0xB0, 0x40, 0xF2, 0x18, 0x00, 0xC0, 0xF2, + 0x00, 0x00, 0x78, 0x44, 0x00, 0x21, 0xC0, 0xF2, + 0x00, 0x01, 0x01, 0x91, 0xFF, 0xF7, 0xF2, 0xFF, + 0x00, 0x21, 0xC0, 0xF2, 0x00, 0x01, 0x00, 0x90, + 0x08, 0x46, 0x02, 0xB0, 0x80, 0xBD, 0x68, 0x65, + 0x6C, 0x6C, 0x6F, 0x0A, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x6D, + 0x0A, 0x00, 0x00, 0xB9, 0x2A, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0xB1, 0x0E, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0xA9, 0x2A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xA1, 0x0E, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0F, 0x01, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5F, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x5F, + 0x70, 0x72, 0x69, 0x6E, 0x74, 0x66, 0x00, 0x00 + }; + std::unique_ptr<NormalizedFile> f = + fromBinary(fileBytes, sizeof(fileBytes), "armv7"); + + EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv7); + EXPECT_EQ((int)(f->fileType), MH_OBJECT); + EXPECT_EQ((int)(f->flags), MH_SUBSECTIONS_VIA_SYMBOLS); + EXPECT_EQ(f->sections.size(), 2UL); + const Section& text = f->sections[0]; + EXPECT_TRUE(text.segmentName.equals("__TEXT")); + EXPECT_TRUE(text.sectionName.equals("__text")); + EXPECT_EQ(text.type, S_REGULAR); + EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS + | S_ATTR_SOME_INSTRUCTIONS)); + EXPECT_EQ(text.alignment, 2U); + EXPECT_EQ(text.address, Hex64(0x0)); + EXPECT_EQ(text.content.size(), 42UL); + EXPECT_EQ((int)(text.content[0]), 0x80); + EXPECT_EQ((int)(text.content[1]), 0xB5); + EXPECT_TRUE(text.indirectSymbols.empty()); + EXPECT_EQ(text.relocations.size(), 5UL); + const Relocation& call = text.relocations[0]; + EXPECT_EQ(call.offset, Hex32(0x18)); + EXPECT_EQ(call.scattered, false); + EXPECT_EQ(call.type, ARM_THUMB_RELOC_BR22); + EXPECT_EQ(call.length, 2); + EXPECT_EQ(call.isExtern, true); + EXPECT_EQ(call.symbol, 1U); + const Relocation& movt = text.relocations[1]; + EXPECT_EQ(movt.offset, Hex32(0xA)); + EXPECT_EQ(movt.scattered, true); + EXPECT_EQ(movt.type, ARM_RELOC_HALF_SECTDIFF); + EXPECT_EQ(movt.length, 3); + EXPECT_EQ(movt.value, Hex32(0x2A)); + const Relocation& movtPair = text.relocations[2]; + EXPECT_EQ(movtPair.offset, Hex32(0x18)); + EXPECT_EQ(movtPair.scattered, true); + EXPECT_EQ(movtPair.type, ARM_RELOC_PAIR); + EXPECT_EQ(movtPair.length, 3); + EXPECT_EQ(movtPair.value, Hex32(0xE)); + const Relocation& movw = text.relocations[3]; + EXPECT_EQ(movw.offset, Hex32(0x6)); + EXPECT_EQ(movw.scattered, true); + EXPECT_EQ(movw.type, ARM_RELOC_HALF_SECTDIFF); + EXPECT_EQ(movw.length, 2); + EXPECT_EQ(movw.value, Hex32(0x2A)); + const Relocation& movwPair = text.relocations[4]; + EXPECT_EQ(movwPair.offset, Hex32(0x0)); + EXPECT_EQ(movwPair.scattered, true); + EXPECT_EQ(movwPair.type, ARM_RELOC_PAIR); + EXPECT_EQ(movwPair.length, 2); + EXPECT_EQ(movwPair.value, Hex32(0xE)); + + const Section& cstring = f->sections[1]; + EXPECT_TRUE(cstring.segmentName.equals("__TEXT")); + EXPECT_TRUE(cstring.sectionName.equals("__cstring")); + EXPECT_EQ(cstring.type, S_CSTRING_LITERALS); + EXPECT_EQ(cstring.attributes, SectionAttr(0)); + EXPECT_EQ(cstring.alignment, 0U); + EXPECT_EQ(cstring.address, Hex64(0x02A)); + EXPECT_EQ(cstring.content.size(), 7UL); + EXPECT_EQ((int)(cstring.content[0]), 0x68); + EXPECT_EQ((int)(cstring.content[1]), 0x65); + EXPECT_EQ((int)(cstring.content[2]), 0x6c); + EXPECT_TRUE(cstring.indirectSymbols.empty()); + EXPECT_TRUE(cstring.relocations.empty()); + + EXPECT_EQ(f->localSymbols.size(), 0UL); + EXPECT_EQ(f->globalSymbols.size(), 1UL); + const Symbol& mainLabel = f->globalSymbols[0]; + EXPECT_TRUE(mainLabel.name.equals("_main")); + EXPECT_EQ(mainLabel.type, N_SECT); + EXPECT_EQ(mainLabel.sect, 1); + EXPECT_EQ(mainLabel.scope, SymbolScope(N_EXT)); + EXPECT_EQ(mainLabel.value, Hex64(0x0)); + EXPECT_EQ(f->undefinedSymbols.size(), 1UL); + const Symbol& printfLabel = f->undefinedSymbols[0]; + EXPECT_TRUE(printfLabel.name.equals("_printf")); + EXPECT_EQ(printfLabel.type, N_UNDF); + EXPECT_EQ(printfLabel.scope, SymbolScope(N_EXT)); +} + + +TEST(BinaryReaderTest, hello_obj_ppc) { + FILEBYTES = { + 0xFE, 0xED, 0xFA, 0xCE, 0x00, 0x00, 0x00, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x28, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x01, 0x44, + 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x5F, 0x5F, 0x74, 0x65, + 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5F, 0x5F, 0x54, 0x45, + 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x01, 0x44, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x90, + 0x00, 0x00, 0x00, 0x05, 0x80, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5F, 0x5F, 0x63, 0x73, 0x74, 0x72, 0x69, 0x6E, + 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5F, 0x5F, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x01, 0xB8, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0xD0, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0B, + 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7C, 0x08, 0x02, 0xA6, + 0xBF, 0xC1, 0xFF, 0xF8, 0x90, 0x01, 0x00, 0x08, + 0x94, 0x21, 0xFF, 0xB0, 0x7C, 0x3E, 0x0B, 0x78, + 0x42, 0x9F, 0x00, 0x05, 0x7F, 0xE8, 0x02, 0xA6, + 0x3C, 0x5F, 0x00, 0x00, 0x38, 0x62, 0x00, 0x2C, + 0x4B, 0xFF, 0xFF, 0xDD, 0x38, 0x00, 0x00, 0x00, + 0x7C, 0x03, 0x03, 0x78, 0x80, 0x21, 0x00, 0x00, + 0x80, 0x01, 0x00, 0x08, 0x7C, 0x08, 0x03, 0xA6, + 0xBB, 0xC1, 0xFF, 0xF8, 0x4E, 0x80, 0x00, 0x20, + 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x01, 0xD3, + 0xAB, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x44, + 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0xAC, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x44, + 0xA1, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x01, 0x0F, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5F, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x5F, + 0x70, 0x72, 0x69, 0x6E, 0x74, 0x66, 0x00, 0x00 + }; + std::unique_ptr<NormalizedFile> f = + fromBinary(fileBytes, sizeof(fileBytes), "ppc"); + + EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_ppc); + EXPECT_EQ((int)(f->fileType), MH_OBJECT); + EXPECT_EQ((int)(f->flags), MH_SUBSECTIONS_VIA_SYMBOLS); + EXPECT_EQ(f->sections.size(), 2UL); + const Section& text = f->sections[0]; + EXPECT_TRUE(text.segmentName.equals("__TEXT")); + EXPECT_TRUE(text.sectionName.equals("__text")); + EXPECT_EQ(text.type, S_REGULAR); + EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS + | S_ATTR_SOME_INSTRUCTIONS)); + EXPECT_EQ(text.alignment, 2U); + EXPECT_EQ(text.address, Hex64(0x0)); + EXPECT_EQ(text.content.size(), 68UL); + EXPECT_EQ((int)(text.content[0]), 0x7C); + EXPECT_EQ((int)(text.content[1]), 0x08); + EXPECT_TRUE(text.indirectSymbols.empty()); + EXPECT_EQ(text.relocations.size(), 5UL); + const Relocation& bl = text.relocations[0]; + EXPECT_EQ(bl.offset, Hex32(0x24)); + EXPECT_EQ(bl.type, PPC_RELOC_BR24); + EXPECT_EQ(bl.length, 2); + EXPECT_EQ(bl.isExtern, true); + EXPECT_EQ(bl.symbol, 1U); + const Relocation& lo = text.relocations[1]; + EXPECT_EQ(lo.offset, Hex32(0x20)); + EXPECT_EQ(lo.scattered, true); + EXPECT_EQ(lo.type, PPC_RELOC_LO16_SECTDIFF); + EXPECT_EQ(lo.length, 2); + EXPECT_EQ(lo.value, Hex32(0x44)); + const Relocation& loPair = text.relocations[2]; + EXPECT_EQ(loPair.offset, Hex32(0x0)); + EXPECT_EQ(loPair.scattered, true); + EXPECT_EQ(loPair.type, PPC_RELOC_PAIR); + EXPECT_EQ(loPair.length, 2); + EXPECT_EQ(loPair.value, Hex32(0x18)); + const Relocation& ha = text.relocations[3]; + EXPECT_EQ(ha.offset, Hex32(0x1C)); + EXPECT_EQ(ha.scattered, true); + EXPECT_EQ(ha.type, PPC_RELOC_HA16_SECTDIFF); + EXPECT_EQ(ha.length, 2); + EXPECT_EQ(ha.value, Hex32(0x44)); + const Relocation& haPair = text.relocations[4]; + EXPECT_EQ(haPair.offset, Hex32(0x2c)); + EXPECT_EQ(haPair.scattered, true); + EXPECT_EQ(haPair.type, PPC_RELOC_PAIR); + EXPECT_EQ(haPair.length, 2); + EXPECT_EQ(haPair.value, Hex32(0x18)); + + const Section& cstring = f->sections[1]; + EXPECT_TRUE(cstring.segmentName.equals("__TEXT")); + EXPECT_TRUE(cstring.sectionName.equals("__cstring")); + EXPECT_EQ(cstring.type, S_CSTRING_LITERALS); + EXPECT_EQ(cstring.attributes, SectionAttr(0)); + EXPECT_EQ(cstring.alignment, 2U); + EXPECT_EQ(cstring.address, Hex64(0x044)); + EXPECT_EQ(cstring.content.size(), 7UL); + EXPECT_EQ((int)(cstring.content[0]), 0x68); + EXPECT_EQ((int)(cstring.content[1]), 0x65); + EXPECT_EQ((int)(cstring.content[2]), 0x6c); + EXPECT_TRUE(cstring.indirectSymbols.empty()); + EXPECT_TRUE(cstring.relocations.empty()); + + EXPECT_EQ(f->localSymbols.size(), 0UL); + EXPECT_EQ(f->globalSymbols.size(), 1UL); + const Symbol& mainLabel = f->globalSymbols[0]; + EXPECT_TRUE(mainLabel.name.equals("_main")); + EXPECT_EQ(mainLabel.type, N_SECT); + EXPECT_EQ(mainLabel.sect, 1); + EXPECT_EQ(mainLabel.scope, SymbolScope(N_EXT)); + EXPECT_EQ(mainLabel.value, Hex64(0x0)); + EXPECT_EQ(f->undefinedSymbols.size(), 1UL); + const Symbol& printfLabel = f->undefinedSymbols[0]; + EXPECT_TRUE(printfLabel.name.equals("_printf")); + EXPECT_EQ(printfLabel.type, N_UNDF); + EXPECT_EQ(printfLabel.scope, SymbolScope(N_EXT)); + + writeBinary(*f, "/tmp/foo.o"); + +} diff --git a/unittests/MachOTests/MachONormalizedFileBinaryWriterTests.cpp b/unittests/MachOTests/MachONormalizedFileBinaryWriterTests.cpp new file mode 100644 index 0000000000000..d79c6d62a8470 --- /dev/null +++ b/unittests/MachOTests/MachONormalizedFileBinaryWriterTests.cpp @@ -0,0 +1,693 @@ +//===- lld/unittest/MachOTests/MachONormalizedFileBinaryWriterTests.cpp ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "../../lib/ReaderWriter/MachO/MachONormalizedFile.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MachO.h" +#include <cassert> +#include <memory> +#include <system_error> +#include <vector> + +using llvm::StringRef; +using llvm::MemoryBuffer; +using llvm::SmallString; +using llvm::Twine; +using llvm::ErrorOr; +using namespace llvm::MachO; +using namespace lld::mach_o::normalized; + +// Parses binary mach-o file at specified path and returns +// ownership of buffer to mb parameter and ownership of +// Normalized file to nf parameter. +static void fromBinary(StringRef path, std::unique_ptr<MemoryBuffer> &mb, + std::unique_ptr<NormalizedFile> &nf, StringRef archStr) { + ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr = MemoryBuffer::getFile(path); + std::error_code ec = mbOrErr.getError(); + EXPECT_FALSE(ec); + mb = std::move(mbOrErr.get()); + + ErrorOr<std::unique_ptr<NormalizedFile>> r = + lld::mach_o::normalized::readBinary( + mb, lld::MachOLinkingContext::archFromName(archStr)); + EXPECT_FALSE(!r); + nf.reset(r->release()); +} + +static Relocation +makeReloc(unsigned addr, bool rel, bool ext, RelocationInfoType type, + unsigned sym) { + Relocation result; + result.offset = addr; + result.scattered = false; + result.type = type; + result.length = 2; + result.pcRel = rel; + result.isExtern = ext; + result.value = 0; + result.symbol = sym; + return result; +} + +static Relocation +makeScatReloc(unsigned addr, RelocationInfoType type, unsigned value) { + Relocation result; + result.offset = addr; + result.scattered = true; + result.type = type; + result.length = 2; + result.pcRel = false; + result.isExtern = true; + result.value = value; + result.symbol = 0; + return result; +} + +static Symbol +makeUndefSymbol(StringRef name) { + Symbol sym; + sym.name = name; + sym.type = N_UNDF; + sym.scope = N_EXT; + sym.sect = NO_SECT; + sym.desc = 0; + sym.value = 0; + return sym; +} + + +static Symbol +makeSymbol(StringRef name, unsigned addr) { + Symbol sym; + sym.name = name; + sym.type = N_SECT; + sym.scope = N_EXT; + sym.sect = 1; + sym.desc = 0; + sym.value = addr; + return sym; +} + +static Symbol +makeThumbSymbol(StringRef name, unsigned addr) { + Symbol sym; + sym.name = name; + sym.type = N_SECT; + sym.scope = N_EXT; + sym.sect = 1; + sym.desc = N_ARM_THUMB_DEF; + sym.value = addr; + return sym; +} + +TEST(BinaryWriterTest, obj_relocs_x86_64) { + SmallString<128> tmpFl; + { + NormalizedFile f; + f.arch = lld::MachOLinkingContext::arch_x86_64; + f.fileType = MH_OBJECT; + f.flags = MH_SUBSECTIONS_VIA_SYMBOLS; + f.os = lld::MachOLinkingContext::OS::macOSX; + f.sections.resize(1); + Section& text = f.sections.front(); + text.segmentName = "__TEXT"; + text.sectionName = "__text"; + text.type = S_REGULAR; + text.attributes = SectionAttr(S_ATTR_PURE_INSTRUCTIONS + | S_ATTR_SOME_INSTRUCTIONS); + text.alignment = 4; + text.address = 0; + const uint8_t textBytes[] = { + 0xe8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x05, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x35, 0x00, 0x00, + 0x00, 0x00, 0x8b, 0x05, 0x00, 0x00, 0x00, 0x00, + 0xc6, 0x05, 0xff, 0xff, 0xff, 0xff, 0x12, 0xc7, + 0x05, 0xfc, 0xff, 0xff, 0xff, 0x78, 0x56, 0x34, + 0x12, 0x48, 0x8b, 0x3d, 0x00, 0x00, 0x00, 0x00 }; + + text.content = llvm::makeArrayRef(textBytes, sizeof(textBytes)); + text.relocations.push_back(makeReloc(0x01, false, true, X86_64_RELOC_BRANCH, 1)); + text.relocations.push_back(makeReloc(0x08, false, true, X86_64_RELOC_GOT_LOAD, 1)); + text.relocations.push_back(makeReloc(0x0E, false, true, X86_64_RELOC_GOT, 1)); + text.relocations.push_back(makeReloc(0x14, false, true, X86_64_RELOC_SIGNED, 1)); + text.relocations.push_back(makeReloc(0x1A, false, true, X86_64_RELOC_SIGNED_1, 1)); + text.relocations.push_back(makeReloc(0x21, false, true, X86_64_RELOC_SIGNED_4, 1)); + text.relocations.push_back(makeReloc(0x2C, false, true, X86_64_RELOC_TLV, 2)); + + f.undefinedSymbols.push_back(makeUndefSymbol("_bar")); + f.undefinedSymbols.push_back(makeUndefSymbol("_tbar")); + + std::error_code ec = + llvm::sys::fs::createTemporaryFile(Twine("xx"), "o", tmpFl); + EXPECT_FALSE(ec); + ec = writeBinary(f, tmpFl); + EXPECT_FALSE(ec); + } + + std::unique_ptr<MemoryBuffer> bufferOwner; + std::unique_ptr<NormalizedFile> f2; + fromBinary(tmpFl, bufferOwner, f2, "x86_64"); + + EXPECT_EQ(lld::MachOLinkingContext::arch_x86_64, f2->arch); + EXPECT_EQ(MH_OBJECT, f2->fileType); + EXPECT_EQ(FileFlags(MH_SUBSECTIONS_VIA_SYMBOLS), f2->flags); + + EXPECT_TRUE(f2->localSymbols.empty()); + EXPECT_TRUE(f2->globalSymbols.empty()); + EXPECT_EQ(2UL, f2->undefinedSymbols.size()); + const Symbol& barUndef = f2->undefinedSymbols[0]; + EXPECT_TRUE(barUndef.name.equals("_bar")); + EXPECT_EQ(N_UNDF, barUndef.type); + EXPECT_EQ(SymbolScope(N_EXT), barUndef.scope); + const Symbol& tbarUndef = f2->undefinedSymbols[1]; + EXPECT_TRUE(tbarUndef.name.equals("_tbar")); + EXPECT_EQ(N_UNDF, tbarUndef.type); + EXPECT_EQ(SymbolScope(N_EXT), tbarUndef.scope); + + EXPECT_EQ(1UL, f2->sections.size()); + const Section& text = f2->sections[0]; + EXPECT_TRUE(text.segmentName.equals("__TEXT")); + EXPECT_TRUE(text.sectionName.equals("__text")); + EXPECT_EQ(S_REGULAR, text.type); + EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS + | S_ATTR_SOME_INSTRUCTIONS)); + EXPECT_EQ(text.alignment, 4U); + EXPECT_EQ(text.address, Hex64(0x0)); + EXPECT_EQ(48UL, text.content.size()); + const Relocation& call = text.relocations[0]; + EXPECT_EQ(call.offset, Hex32(0x1)); + EXPECT_EQ(call.type, X86_64_RELOC_BRANCH); + EXPECT_EQ(call.length, 2); + EXPECT_EQ(call.isExtern, true); + EXPECT_EQ(call.symbol, 1U); + const Relocation& gotLoad = text.relocations[1]; + EXPECT_EQ(gotLoad.offset, Hex32(0x8)); + EXPECT_EQ(gotLoad.type, X86_64_RELOC_GOT_LOAD); + EXPECT_EQ(gotLoad.length, 2); + EXPECT_EQ(gotLoad.isExtern, true); + EXPECT_EQ(gotLoad.symbol, 1U); + const Relocation& gotUse = text.relocations[2]; + EXPECT_EQ(gotUse.offset, Hex32(0xE)); + EXPECT_EQ(gotUse.type, X86_64_RELOC_GOT); + EXPECT_EQ(gotUse.length, 2); + EXPECT_EQ(gotUse.isExtern, true); + EXPECT_EQ(gotUse.symbol, 1U); + const Relocation& signed0 = text.relocations[3]; + EXPECT_EQ(signed0.offset, Hex32(0x14)); + EXPECT_EQ(signed0.type, X86_64_RELOC_SIGNED); + EXPECT_EQ(signed0.length, 2); + EXPECT_EQ(signed0.isExtern, true); + EXPECT_EQ(signed0.symbol, 1U); + const Relocation& signed1 = text.relocations[4]; + EXPECT_EQ(signed1.offset, Hex32(0x1A)); + EXPECT_EQ(signed1.type, X86_64_RELOC_SIGNED_1); + EXPECT_EQ(signed1.length, 2); + EXPECT_EQ(signed1.isExtern, true); + EXPECT_EQ(signed1.symbol, 1U); + const Relocation& signed4 = text.relocations[5]; + EXPECT_EQ(signed4.offset, Hex32(0x21)); + EXPECT_EQ(signed4.type, X86_64_RELOC_SIGNED_4); + EXPECT_EQ(signed4.length, 2); + EXPECT_EQ(signed4.isExtern, true); + EXPECT_EQ(signed4.symbol, 1U); + + std::error_code ec = llvm::sys::fs::remove(Twine(tmpFl)); + EXPECT_FALSE(ec); +} + + + +TEST(BinaryWriterTest, obj_relocs_x86) { + SmallString<128> tmpFl; + { + NormalizedFile f; + f.arch = lld::MachOLinkingContext::arch_x86; + f.fileType = MH_OBJECT; + f.flags = MH_SUBSECTIONS_VIA_SYMBOLS; + f.os = lld::MachOLinkingContext::OS::macOSX; + f.sections.resize(1); + Section& text = f.sections.front(); + text.segmentName = "__TEXT"; + text.sectionName = "__text"; + text.type = S_REGULAR; + text.attributes = SectionAttr(S_ATTR_PURE_INSTRUCTIONS + | S_ATTR_SOME_INSTRUCTIONS); + text.alignment = 4; + text.address = 0; + const uint8_t textBytes[] = { + 0xe8, 0xfb, 0xff, 0xff, 0xff, 0xa1, 0x00, 0x00, + 0x00, 0x00, 0x8b, 0xb0, 0xfb, 0xff, 0xff, 0xff, + 0x8b, 0x80, 0x11, 0x00, 0x00, 0x00 }; + + text.content = llvm::makeArrayRef(textBytes, sizeof(textBytes)); + text.relocations.push_back(makeReloc(0x01, true, true, GENERIC_RELOC_VANILLA, 0)); + text.relocations.push_back(makeReloc(0x06, false, true, GENERIC_RELOC_VANILLA, 0)); + text.relocations.push_back(makeScatReloc(0x0c, GENERIC_RELOC_LOCAL_SECTDIFF, 0)); + text.relocations.push_back(makeScatReloc(0x0, GENERIC_RELOC_PAIR, 5)); + text.relocations.push_back(makeReloc(0x12, true, true, GENERIC_RELOC_TLV, 1)); + + f.undefinedSymbols.push_back(makeUndefSymbol("_bar")); + f.undefinedSymbols.push_back(makeUndefSymbol("_tbar")); + + std::error_code ec = + llvm::sys::fs::createTemporaryFile(Twine("xx"), "o", tmpFl); + EXPECT_FALSE(ec); + ec = writeBinary(f, tmpFl); + EXPECT_FALSE(ec); + } + std::unique_ptr<MemoryBuffer> bufferOwner; + std::unique_ptr<NormalizedFile> f2; + fromBinary(tmpFl, bufferOwner, f2, "i386"); + + EXPECT_EQ(lld::MachOLinkingContext::arch_x86, f2->arch); + EXPECT_EQ(MH_OBJECT, f2->fileType); + EXPECT_EQ(FileFlags(MH_SUBSECTIONS_VIA_SYMBOLS), f2->flags); + + EXPECT_TRUE(f2->localSymbols.empty()); + EXPECT_TRUE(f2->globalSymbols.empty()); + EXPECT_EQ(2UL, f2->undefinedSymbols.size()); + const Symbol& barUndef = f2->undefinedSymbols[0]; + EXPECT_TRUE(barUndef.name.equals("_bar")); + EXPECT_EQ(N_UNDF, barUndef.type); + EXPECT_EQ(SymbolScope(N_EXT), barUndef.scope); + const Symbol& tbarUndef = f2->undefinedSymbols[1]; + EXPECT_TRUE(tbarUndef.name.equals("_tbar")); + EXPECT_EQ(N_UNDF, tbarUndef.type); + EXPECT_EQ(SymbolScope(N_EXT), tbarUndef.scope); + + EXPECT_EQ(1UL, f2->sections.size()); + const Section& text = f2->sections[0]; + EXPECT_TRUE(text.segmentName.equals("__TEXT")); + EXPECT_TRUE(text.sectionName.equals("__text")); + EXPECT_EQ(S_REGULAR, text.type); + EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS + | S_ATTR_SOME_INSTRUCTIONS)); + EXPECT_EQ(text.alignment, 4U); + EXPECT_EQ(text.address, Hex64(0x0)); + EXPECT_EQ(22UL, text.content.size()); + const Relocation& call = text.relocations[0]; + EXPECT_EQ(call.offset, Hex32(0x1)); + EXPECT_EQ(call.scattered, false); + EXPECT_EQ(call.type, GENERIC_RELOC_VANILLA); + EXPECT_EQ(call.pcRel, true); + EXPECT_EQ(call.length, 2); + EXPECT_EQ(call.isExtern, true); + EXPECT_EQ(call.symbol, 0U); + const Relocation& absLoad = text.relocations[1]; + EXPECT_EQ(absLoad.offset, Hex32(0x6)); + EXPECT_EQ(absLoad.scattered, false); + EXPECT_EQ(absLoad.type, GENERIC_RELOC_VANILLA); + EXPECT_EQ(absLoad.pcRel, false); + EXPECT_EQ(absLoad.length, 2); + EXPECT_EQ(absLoad.isExtern, true); + EXPECT_EQ(absLoad.symbol,0U); + const Relocation& pic1 = text.relocations[2]; + EXPECT_EQ(pic1.offset, Hex32(0xc)); + EXPECT_EQ(pic1.scattered, true); + EXPECT_EQ(pic1.type, GENERIC_RELOC_LOCAL_SECTDIFF); + EXPECT_EQ(pic1.length, 2); + EXPECT_EQ(pic1.value, 0U); + const Relocation& pic2 = text.relocations[3]; + EXPECT_EQ(pic2.offset, Hex32(0x0)); + EXPECT_EQ(pic1.scattered, true); + EXPECT_EQ(pic2.type, GENERIC_RELOC_PAIR); + EXPECT_EQ(pic2.length, 2); + EXPECT_EQ(pic2.value, 5U); + const Relocation& tlv = text.relocations[4]; + EXPECT_EQ(tlv.offset, Hex32(0x12)); + EXPECT_EQ(tlv.type, GENERIC_RELOC_TLV); + EXPECT_EQ(tlv.length, 2); + EXPECT_EQ(tlv.isExtern, true); + EXPECT_EQ(tlv.symbol, 1U); + + //llvm::errs() << "temp = " << tmpFl << "\n"; + std::error_code ec = llvm::sys::fs::remove(Twine(tmpFl)); + EXPECT_FALSE(ec); +} + + + +TEST(BinaryWriterTest, obj_relocs_armv7) { + SmallString<128> tmpFl; + { + NormalizedFile f; + f.arch = lld::MachOLinkingContext::arch_armv7; + f.fileType = MH_OBJECT; + f.flags = MH_SUBSECTIONS_VIA_SYMBOLS; + f.os = lld::MachOLinkingContext::OS::macOSX; + f.sections.resize(1); + Section& text = f.sections.front(); + text.segmentName = "__TEXT"; + text.sectionName = "__text"; + text.type = S_REGULAR; + text.attributes = SectionAttr(S_ATTR_PURE_INSTRUCTIONS + | S_ATTR_SOME_INSTRUCTIONS); + text.alignment = 2; + text.address = 0; + const uint8_t textBytes[] = { + 0xff, 0xf7, 0xfe, 0xef, 0x40, 0xf2, 0x05, 0x01, + 0xc0, 0xf2, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbf }; + + text.content = llvm::makeArrayRef(textBytes, sizeof(textBytes)); + text.relocations.push_back(makeReloc(0x00, true, true, + ARM_THUMB_RELOC_BR22, 2)); + text.relocations.push_back(makeScatReloc(0x04, + ARM_RELOC_HALF_SECTDIFF, 0x10)); + text.relocations.push_back(makeScatReloc(0x00, + ARM_RELOC_PAIR, 0xC)); + text.relocations.push_back(makeScatReloc(0x08, + ARM_RELOC_HALF_SECTDIFF, 0x10)); + text.relocations.push_back(makeScatReloc(0x00, + ARM_RELOC_PAIR, 0xC)); + text.relocations.push_back(makeReloc(0x0C, false, true, + ARM_RELOC_VANILLA, 2)); + + f.globalSymbols.push_back(makeThumbSymbol("_foo", 0x00)); + f.globalSymbols.push_back(makeThumbSymbol("_foo2", 0x10)); + f.undefinedSymbols.push_back(makeUndefSymbol("_bar")); + + std::error_code ec = + llvm::sys::fs::createTemporaryFile(Twine("xx"), "o", tmpFl); + EXPECT_FALSE(ec); + ec = writeBinary(f, tmpFl); + EXPECT_FALSE(ec); + } + std::unique_ptr<MemoryBuffer> bufferOwner; + std::unique_ptr<NormalizedFile> f2; + fromBinary(tmpFl, bufferOwner, f2, "armv7"); + + EXPECT_EQ(lld::MachOLinkingContext::arch_armv7, f2->arch); + EXPECT_EQ(MH_OBJECT, f2->fileType); + EXPECT_EQ(FileFlags(MH_SUBSECTIONS_VIA_SYMBOLS), f2->flags); + + EXPECT_TRUE(f2->localSymbols.empty()); + EXPECT_EQ(2UL, f2->globalSymbols.size()); + const Symbol& fooDef = f2->globalSymbols[0]; + EXPECT_TRUE(fooDef.name.equals("_foo")); + EXPECT_EQ(N_SECT, fooDef.type); + EXPECT_EQ(1, fooDef.sect); + EXPECT_EQ(SymbolScope(N_EXT), fooDef.scope); + const Symbol& foo2Def = f2->globalSymbols[1]; + EXPECT_TRUE(foo2Def.name.equals("_foo2")); + EXPECT_EQ(N_SECT, foo2Def.type); + EXPECT_EQ(1, foo2Def.sect); + EXPECT_EQ(SymbolScope(N_EXT), foo2Def.scope); + + EXPECT_EQ(1UL, f2->undefinedSymbols.size()); + const Symbol& barUndef = f2->undefinedSymbols[0]; + EXPECT_TRUE(barUndef.name.equals("_bar")); + EXPECT_EQ(N_UNDF, barUndef.type); + EXPECT_EQ(SymbolScope(N_EXT), barUndef.scope); + + EXPECT_EQ(1UL, f2->sections.size()); + const Section& text = f2->sections[0]; + EXPECT_TRUE(text.segmentName.equals("__TEXT")); + EXPECT_TRUE(text.sectionName.equals("__text")); + EXPECT_EQ(S_REGULAR, text.type); + EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS + | S_ATTR_SOME_INSTRUCTIONS)); + EXPECT_EQ(text.alignment, 2U); + EXPECT_EQ(text.address, Hex64(0x0)); + EXPECT_EQ(18UL, text.content.size()); + const Relocation& blx = text.relocations[0]; + EXPECT_EQ(blx.offset, Hex32(0x0)); + EXPECT_EQ(blx.scattered, false); + EXPECT_EQ(blx.type, ARM_THUMB_RELOC_BR22); + EXPECT_EQ(blx.pcRel, true); + EXPECT_EQ(blx.length, 2); + EXPECT_EQ(blx.isExtern, true); + EXPECT_EQ(blx.symbol, 2U); + const Relocation& movw1 = text.relocations[1]; + EXPECT_EQ(movw1.offset, Hex32(0x4)); + EXPECT_EQ(movw1.scattered, true); + EXPECT_EQ(movw1.type, ARM_RELOC_HALF_SECTDIFF); + EXPECT_EQ(movw1.length, 2); + EXPECT_EQ(movw1.value, 0x10U); + const Relocation& movw2 = text.relocations[2]; + EXPECT_EQ(movw2.offset, Hex32(0x0)); + EXPECT_EQ(movw2.scattered, true); + EXPECT_EQ(movw2.type, ARM_RELOC_PAIR); + EXPECT_EQ(movw2.length, 2); + EXPECT_EQ(movw2.value, Hex32(0xC)); + const Relocation& movt1 = text.relocations[3]; + EXPECT_EQ(movt1.offset, Hex32(0x8)); + EXPECT_EQ(movt1.scattered, true); + EXPECT_EQ(movt1.type, ARM_RELOC_HALF_SECTDIFF); + EXPECT_EQ(movt1.length, 2); + EXPECT_EQ(movt1.value, Hex32(0x10)); + const Relocation& movt2 = text.relocations[4]; + EXPECT_EQ(movt2.offset, Hex32(0x0)); + EXPECT_EQ(movt2.scattered, true); + EXPECT_EQ(movt2.type, ARM_RELOC_PAIR); + EXPECT_EQ(movt2.length, 2); + EXPECT_EQ(movt2.value, Hex32(0xC)); + const Relocation& absPointer = text.relocations[5]; + EXPECT_EQ(absPointer.offset, Hex32(0xC)); + EXPECT_EQ(absPointer.type, ARM_RELOC_VANILLA); + EXPECT_EQ(absPointer.length, 2); + EXPECT_EQ(absPointer.isExtern, true); + EXPECT_EQ(absPointer.symbol, 2U); + + //llvm::errs() << "temp = " << tmpFl << "\n"; + std::error_code ec = llvm::sys::fs::remove(Twine(tmpFl)); + EXPECT_FALSE(ec); +} + + + +TEST(BinaryWriterTest, obj_relocs_ppc) { + SmallString<128> tmpFl; + { + NormalizedFile f; + f.arch = lld::MachOLinkingContext::arch_ppc; + f.fileType = MH_OBJECT; + f.flags = MH_SUBSECTIONS_VIA_SYMBOLS; + f.os = lld::MachOLinkingContext::OS::macOSX; + f.sections.resize(1); + Section& text = f.sections.front(); + text.segmentName = "__TEXT"; + text.sectionName = "__text"; + text.type = S_REGULAR; + text.attributes = SectionAttr(S_ATTR_PURE_INSTRUCTIONS + | S_ATTR_SOME_INSTRUCTIONS); + text.alignment = 2; + text.address = 0; + const uint8_t textBytes[] = { + 0x48, 0x00, 0x00, 0x01, 0x40, 0x82, 0xff, 0xfc, + 0x3c, 0x62, 0x00, 0x00, 0x3c, 0x62, 0x00, 0x00, + 0x80, 0x63, 0x00, 0x24, 0x80, 0x63, 0x00, 0x24, + 0x3c, 0x40, 0x00, 0x00, 0x3c, 0x60, 0x00, 0x00, + 0x80, 0x42, 0x00, 0x28, 0x80, 0x63, 0x00, 0x28, + 0x60, 0x00, 0x00, 0x00 }; + + text.content = llvm::makeArrayRef(textBytes, sizeof(textBytes)); + text.relocations.push_back(makeReloc(0x00, true, true, + PPC_RELOC_BR24, 2)); + text.relocations.push_back(makeReloc(0x04, true, true, + PPC_RELOC_BR14, 2)); + text.relocations.push_back(makeScatReloc(0x08, + PPC_RELOC_HI16_SECTDIFF, 0x28)); + text.relocations.push_back(makeScatReloc(0x24, + PPC_RELOC_PAIR, 0x4)); + text.relocations.push_back(makeScatReloc(0x0C, + PPC_RELOC_HA16_SECTDIFF, 0x28)); + text.relocations.push_back(makeScatReloc(0x24, + PPC_RELOC_PAIR, 0x4)); + text.relocations.push_back(makeScatReloc(0x10, + PPC_RELOC_LO16_SECTDIFF, 0x28)); + text.relocations.push_back(makeScatReloc(0x00, + PPC_RELOC_PAIR, 0x4)); + text.relocations.push_back(makeScatReloc(0x14, + PPC_RELOC_LO14_SECTDIFF, 0x28)); + text.relocations.push_back(makeScatReloc(0x00, + PPC_RELOC_PAIR, 0x4)); + text.relocations.push_back(makeReloc(0x18, false, false, + PPC_RELOC_HI16, 1)); + text.relocations.push_back(makeReloc(0x28, false, false, + PPC_RELOC_PAIR, 0)); + text.relocations.push_back(makeReloc(0x1C, false, false, + PPC_RELOC_HA16, 1)); + text.relocations.push_back(makeReloc(0x28, false, false, + PPC_RELOC_PAIR, 0)); + text.relocations.push_back(makeReloc(0x20, false, false, + PPC_RELOC_LO16, 1)); + text.relocations.push_back(makeReloc(0x00, false, false, + PPC_RELOC_PAIR, 0)); + text.relocations.push_back(makeReloc(0x24, false, false, + PPC_RELOC_LO14, 1)); + text.relocations.push_back(makeReloc(0x00, false, false, + PPC_RELOC_PAIR, 0)); + + f.globalSymbols.push_back(makeSymbol("_foo", 0x00)); + f.globalSymbols.push_back(makeSymbol("_foo2", 0x28)); + f.undefinedSymbols.push_back(makeUndefSymbol("_bar")); + + std::error_code ec = + llvm::sys::fs::createTemporaryFile(Twine("xx"), "o", tmpFl); + EXPECT_FALSE(ec); + ec = writeBinary(f, tmpFl); + EXPECT_FALSE(ec); + } + std::unique_ptr<MemoryBuffer> bufferOwner; + std::unique_ptr<NormalizedFile> f2; + fromBinary(tmpFl, bufferOwner, f2, "ppc"); + + EXPECT_EQ(lld::MachOLinkingContext::arch_ppc, f2->arch); + EXPECT_EQ(MH_OBJECT, f2->fileType); + EXPECT_EQ(FileFlags(MH_SUBSECTIONS_VIA_SYMBOLS), f2->flags); + + EXPECT_TRUE(f2->localSymbols.empty()); + EXPECT_EQ(2UL, f2->globalSymbols.size()); + const Symbol& fooDef = f2->globalSymbols[0]; + EXPECT_TRUE(fooDef.name.equals("_foo")); + EXPECT_EQ(N_SECT, fooDef.type); + EXPECT_EQ(1, fooDef.sect); + EXPECT_EQ(SymbolScope(N_EXT), fooDef.scope); + const Symbol& foo2Def = f2->globalSymbols[1]; + EXPECT_TRUE(foo2Def.name.equals("_foo2")); + EXPECT_EQ(N_SECT, foo2Def.type); + EXPECT_EQ(1, foo2Def.sect); + EXPECT_EQ(SymbolScope(N_EXT), foo2Def.scope); + + EXPECT_EQ(1UL, f2->undefinedSymbols.size()); + const Symbol& barUndef = f2->undefinedSymbols[0]; + EXPECT_TRUE(barUndef.name.equals("_bar")); + EXPECT_EQ(N_UNDF, barUndef.type); + EXPECT_EQ(SymbolScope(N_EXT), barUndef.scope); + + EXPECT_EQ(1UL, f2->sections.size()); + const Section& text = f2->sections[0]; + EXPECT_TRUE(text.segmentName.equals("__TEXT")); + EXPECT_TRUE(text.sectionName.equals("__text")); + EXPECT_EQ(S_REGULAR, text.type); + EXPECT_EQ(text.attributes,SectionAttr(S_ATTR_PURE_INSTRUCTIONS + | S_ATTR_SOME_INSTRUCTIONS)); + EXPECT_EQ(text.alignment, 2U); + EXPECT_EQ(text.address, Hex64(0x0)); + EXPECT_EQ(44UL, text.content.size()); + const Relocation& br24 = text.relocations[0]; + EXPECT_EQ(br24.offset, Hex32(0x0)); + EXPECT_EQ(br24.scattered, false); + EXPECT_EQ(br24.type, PPC_RELOC_BR24); + EXPECT_EQ(br24.pcRel, true); + EXPECT_EQ(br24.length, 2); + EXPECT_EQ(br24.isExtern, true); + EXPECT_EQ(br24.symbol, 2U); + const Relocation& br14 = text.relocations[1]; + EXPECT_EQ(br14.offset, Hex32(0x4)); + EXPECT_EQ(br14.scattered, false); + EXPECT_EQ(br14.type, PPC_RELOC_BR14); + EXPECT_EQ(br14.pcRel, true); + EXPECT_EQ(br14.length, 2); + EXPECT_EQ(br14.isExtern, true); + EXPECT_EQ(br14.symbol, 2U); + const Relocation& pichi1 = text.relocations[2]; + EXPECT_EQ(pichi1.offset, Hex32(0x8)); + EXPECT_EQ(pichi1.scattered, true); + EXPECT_EQ(pichi1.type, PPC_RELOC_HI16_SECTDIFF); + EXPECT_EQ(pichi1.length, 2); + EXPECT_EQ(pichi1.value, 0x28U); + const Relocation& pichi2 = text.relocations[3]; + EXPECT_EQ(pichi2.offset, Hex32(0x24)); + EXPECT_EQ(pichi2.scattered, true); + EXPECT_EQ(pichi2.type, PPC_RELOC_PAIR); + EXPECT_EQ(pichi2.length, 2); + EXPECT_EQ(pichi2.value, 0x4U); + const Relocation& picha1 = text.relocations[4]; + EXPECT_EQ(picha1.offset, Hex32(0xC)); + EXPECT_EQ(picha1.scattered, true); + EXPECT_EQ(picha1.type, PPC_RELOC_HA16_SECTDIFF); + EXPECT_EQ(picha1.length, 2); + EXPECT_EQ(picha1.value, 0x28U); + const Relocation& picha2 = text.relocations[5]; + EXPECT_EQ(picha2.offset, Hex32(0x24)); + EXPECT_EQ(picha2.scattered, true); + EXPECT_EQ(picha2.type, PPC_RELOC_PAIR); + EXPECT_EQ(picha2.length, 2); + EXPECT_EQ(picha2.value, 0x4U); + const Relocation& piclo1 = text.relocations[6]; + EXPECT_EQ(piclo1.offset, Hex32(0x10)); + EXPECT_EQ(piclo1.scattered, true); + EXPECT_EQ(piclo1.type, PPC_RELOC_LO16_SECTDIFF); + EXPECT_EQ(piclo1.length, 2); + EXPECT_EQ(piclo1.value, 0x28U); + const Relocation& piclo2 = text.relocations[7]; + EXPECT_EQ(piclo2.offset, Hex32(0x0)); + EXPECT_EQ(piclo2.scattered, true); + EXPECT_EQ(piclo2.type, PPC_RELOC_PAIR); + EXPECT_EQ(piclo2.length, 2); + EXPECT_EQ(piclo2.value, 0x4U); + const Relocation& picloa1 = text.relocations[8]; + EXPECT_EQ(picloa1.offset, Hex32(0x14)); + EXPECT_EQ(picloa1.scattered, true); + EXPECT_EQ(picloa1.type, PPC_RELOC_LO14_SECTDIFF); + EXPECT_EQ(picloa1.length, 2); + EXPECT_EQ(picloa1.value, 0x28U); + const Relocation& picloa2 = text.relocations[9]; + EXPECT_EQ(picloa2.offset, Hex32(0x0)); + EXPECT_EQ(picloa2.scattered, true); + EXPECT_EQ(picloa2.type, PPC_RELOC_PAIR); + EXPECT_EQ(picloa2.length, 2); + EXPECT_EQ(picloa2.value, 0x4U); + const Relocation& abshi1 = text.relocations[10]; + EXPECT_EQ(abshi1.offset, Hex32(0x18)); + EXPECT_EQ(abshi1.scattered, false); + EXPECT_EQ(abshi1.type, PPC_RELOC_HI16); + EXPECT_EQ(abshi1.length, 2); + EXPECT_EQ(abshi1.symbol, 1U); + const Relocation& abshi2 = text.relocations[11]; + EXPECT_EQ(abshi2.offset, Hex32(0x28)); + EXPECT_EQ(abshi2.scattered, false); + EXPECT_EQ(abshi2.type, PPC_RELOC_PAIR); + EXPECT_EQ(abshi2.length, 2); + EXPECT_EQ(abshi2.symbol, 0U); + const Relocation& absha1 = text.relocations[12]; + EXPECT_EQ(absha1.offset, Hex32(0x1C)); + EXPECT_EQ(absha1.scattered, false); + EXPECT_EQ(absha1.type, PPC_RELOC_HA16); + EXPECT_EQ(absha1.length, 2); + EXPECT_EQ(absha1.symbol, 1U); + const Relocation& absha2 = text.relocations[13]; + EXPECT_EQ(absha2.offset, Hex32(0x28)); + EXPECT_EQ(absha2.scattered, false); + EXPECT_EQ(absha2.type, PPC_RELOC_PAIR); + EXPECT_EQ(absha2.length, 2); + EXPECT_EQ(absha2.symbol, 0U); + const Relocation& abslo1 = text.relocations[14]; + EXPECT_EQ(abslo1.offset, Hex32(0x20)); + EXPECT_EQ(abslo1.scattered, false); + EXPECT_EQ(abslo1.type, PPC_RELOC_LO16); + EXPECT_EQ(abslo1.length, 2); + EXPECT_EQ(abslo1.symbol, 1U); + const Relocation& abslo2 = text.relocations[15]; + EXPECT_EQ(abslo2.offset, Hex32(0x00)); + EXPECT_EQ(abslo2.scattered, false); + EXPECT_EQ(abslo2.type, PPC_RELOC_PAIR); + EXPECT_EQ(abslo2.length, 2); + EXPECT_EQ(abslo2.symbol, 0U); + const Relocation& absloa1 = text.relocations[16]; + EXPECT_EQ(absloa1.offset, Hex32(0x24)); + EXPECT_EQ(absloa1.scattered, false); + EXPECT_EQ(absloa1.type, PPC_RELOC_LO14); + EXPECT_EQ(absloa1.length, 2); + EXPECT_EQ(absloa1.symbol, 1U); + const Relocation& absloa2 = text.relocations[17]; + EXPECT_EQ(absloa2.offset, Hex32(0x00)); + EXPECT_EQ(absloa2.scattered, false); + EXPECT_EQ(absloa2.type, PPC_RELOC_PAIR); + EXPECT_EQ(absloa2.length, 2); + EXPECT_EQ(absloa2.symbol, 0U); + + std::error_code ec = llvm::sys::fs::remove(Twine(tmpFl)); + EXPECT_FALSE(ec); +} + diff --git a/unittests/MachOTests/MachONormalizedFileToAtomsTests.cpp b/unittests/MachOTests/MachONormalizedFileToAtomsTests.cpp new file mode 100644 index 0000000000000..16ef88aa4a40e --- /dev/null +++ b/unittests/MachOTests/MachONormalizedFileToAtomsTests.cpp @@ -0,0 +1,94 @@ +//===- lld/unittest/MachOTests/MachONormalizedFileToAtomsTests.cpp --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "../../lib/ReaderWriter/MachO/MachONormalizedFile.h" +#include "llvm/Support/MachO.h" +#include <assert.h> +#include <vector> + +using llvm::ErrorOr; + +using namespace lld::mach_o::normalized; +using namespace llvm::MachO; + +TEST(ToAtomsTest, empty_obj_x86_64) { + NormalizedFile f; + f.arch = lld::MachOLinkingContext::arch_x86_64; + ErrorOr<std::unique_ptr<const lld::File>> atom_f = + normalizedToAtoms(f, "", false); + EXPECT_FALSE(!atom_f); + EXPECT_EQ(0U, (*atom_f)->defined().size()); +} + +TEST(ToAtomsTest, basic_obj_x86_64) { + NormalizedFile f; + f.arch = lld::MachOLinkingContext::arch_x86_64; + Section textSection; + static const uint8_t contentBytes[] = { 0x90, 0xC3, 0xC3, 0xC4 }; + const unsigned contentSize = sizeof(contentBytes) / sizeof(contentBytes[0]); + textSection.content = llvm::makeArrayRef(contentBytes, contentSize); + f.sections.push_back(textSection); + Symbol fooSymbol; + fooSymbol.name = "_foo"; + fooSymbol.type = N_SECT; + fooSymbol.scope = N_EXT; + fooSymbol.sect = 1; + fooSymbol.value = 0; + f.globalSymbols.push_back(fooSymbol); + Symbol barSymbol; + barSymbol.name = "_bar"; + barSymbol.type = N_SECT; + barSymbol.scope = N_EXT; + barSymbol.sect = 1; + barSymbol.value = 2; + f.globalSymbols.push_back(barSymbol); + Symbol undefSym; + undefSym.name = "_undef"; + undefSym.type = N_UNDF; + f.undefinedSymbols.push_back(undefSym); + Symbol bazSymbol; + bazSymbol.name = "_baz"; + bazSymbol.type = N_SECT; + bazSymbol.scope = N_EXT | N_PEXT; + bazSymbol.sect = 1; + bazSymbol.value = 3; + f.localSymbols.push_back(bazSymbol); + + ErrorOr<std::unique_ptr<const lld::File>> atom_f = + normalizedToAtoms(f, "", false); + EXPECT_FALSE(!atom_f); + const lld::File &file = **atom_f; + EXPECT_EQ(3U, file.defined().size()); + lld::File::defined_iterator it = file.defined().begin(); + const lld::DefinedAtom *atom1 = *it; + ++it; + const lld::DefinedAtom *atom2 = *it; + ++it; + const lld::DefinedAtom *atom3 = *it; + const lld::UndefinedAtom *atom4 = *file.undefined().begin(); + EXPECT_TRUE(atom1->name().equals("_foo")); + EXPECT_EQ(2U, atom1->rawContent().size()); + EXPECT_EQ(0x90, atom1->rawContent()[0]); + EXPECT_EQ(0xC3, atom1->rawContent()[1]); + EXPECT_EQ(lld::Atom::scopeGlobal, atom1->scope()); + + EXPECT_TRUE(atom2->name().equals("_bar")); + EXPECT_EQ(1U, atom2->rawContent().size()); + EXPECT_EQ(0xC3, atom2->rawContent()[0]); + EXPECT_EQ(lld::Atom::scopeGlobal, atom2->scope()); + + EXPECT_TRUE(atom3->name().equals("_baz")); + EXPECT_EQ(1U, atom3->rawContent().size()); + EXPECT_EQ(0xC4, atom3->rawContent()[0]); + EXPECT_EQ(lld::Atom::scopeLinkageUnit, atom3->scope()); + + EXPECT_TRUE(atom4->name().equals("_undef")); + EXPECT_EQ(lld::Atom::definitionUndefined, atom4->definition()); +} diff --git a/unittests/MachOTests/MachONormalizedFileYAMLTests.cpp b/unittests/MachOTests/MachONormalizedFileYAMLTests.cpp new file mode 100644 index 0000000000000..562dae60049dc --- /dev/null +++ b/unittests/MachOTests/MachONormalizedFileYAMLTests.cpp @@ -0,0 +1,766 @@ +//===- lld/unittest/MachOTests/MachONormalizedFileYAMLTests.cpp -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "../../lib/ReaderWriter/MachO/MachONormalizedFile.h" +#include "llvm/Support/MachO.h" +#include <assert.h> +#include <vector> + +using llvm::StringRef; +using llvm::MemoryBuffer; +using llvm::ErrorOr; +using lld::mach_o::normalized::NormalizedFile; +using lld::mach_o::normalized::Symbol; +using lld::mach_o::normalized::Section; +using lld::mach_o::normalized::Relocation; + + +static std::unique_ptr<NormalizedFile> fromYAML(StringRef str) { + std::unique_ptr<MemoryBuffer> mb(MemoryBuffer::getMemBuffer(str)); + ErrorOr<std::unique_ptr<NormalizedFile>> r + = lld::mach_o::normalized::readYaml(mb); + EXPECT_FALSE(!r); + return std::move(*r); +} + +static void toYAML(const NormalizedFile &f, std::string &out) { + llvm::raw_string_ostream ostr(out); + std::error_code ec = lld::mach_o::normalized::writeYaml(f, ostr); + EXPECT_TRUE(!ec); +} + + +// ppc is no longer supported, but it is here to test endianness handling. +TEST(ObjectFileYAML, empty_ppc) { + std::unique_ptr<NormalizedFile> f = fromYAML( + "---\n" + "arch: ppc\n" + "file-type: MH_OBJECT\n" + "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n" + "...\n"); + EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_ppc); + EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT); + EXPECT_EQ((int)(f->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS); + EXPECT_TRUE(f->sections.empty()); + EXPECT_TRUE(f->localSymbols.empty()); + EXPECT_TRUE(f->globalSymbols.empty()); + EXPECT_TRUE(f->undefinedSymbols.empty()); +} + +TEST(ObjectFileYAML, empty_x86_64) { + std::unique_ptr<NormalizedFile> f = fromYAML( + "---\n" + "arch: x86_64\n" + "file-type: MH_OBJECT\n" + "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n" + "...\n"); + EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64); + EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT); + EXPECT_EQ((int)(f->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS); + EXPECT_TRUE(f->sections.empty()); + EXPECT_TRUE(f->localSymbols.empty()); + EXPECT_TRUE(f->globalSymbols.empty()); + EXPECT_TRUE(f->undefinedSymbols.empty()); +} + +TEST(ObjectFileYAML, empty_x86) { + std::unique_ptr<NormalizedFile> f = fromYAML( + "---\n" + "arch: x86\n" + "file-type: MH_OBJECT\n" + "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n" + "...\n"); + EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86); + EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT); + EXPECT_EQ((int)(f->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS); + EXPECT_TRUE(f->sections.empty()); + EXPECT_TRUE(f->localSymbols.empty()); + EXPECT_TRUE(f->globalSymbols.empty()); + EXPECT_TRUE(f->undefinedSymbols.empty()); +} + +TEST(ObjectFileYAML, empty_armv6) { + std::unique_ptr<NormalizedFile> f = fromYAML( + "---\n" + "arch: armv6\n" + "file-type: MH_OBJECT\n" + "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n" + "...\n"); + EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv6); + EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT); + EXPECT_EQ((int)(f->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS); + EXPECT_TRUE(f->sections.empty()); + EXPECT_TRUE(f->localSymbols.empty()); + EXPECT_TRUE(f->globalSymbols.empty()); + EXPECT_TRUE(f->undefinedSymbols.empty()); +} + +TEST(ObjectFileYAML, empty_armv7) { + std::unique_ptr<NormalizedFile> f = fromYAML( + "---\n" + "arch: armv7\n" + "file-type: MH_OBJECT\n" + "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n" + "...\n"); + EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv7); + EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT); + EXPECT_EQ((int)(f->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS); + EXPECT_TRUE(f->sections.empty()); + EXPECT_TRUE(f->localSymbols.empty()); + EXPECT_TRUE(f->globalSymbols.empty()); + EXPECT_TRUE(f->undefinedSymbols.empty()); +} + +TEST(ObjectFileYAML, empty_armv7s) { + std::unique_ptr<NormalizedFile> f = fromYAML( + "---\n" + "arch: armv7s\n" + "file-type: MH_OBJECT\n" + "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n" + "...\n"); + EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv7s); + EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT); + EXPECT_EQ((int)(f->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS); + EXPECT_TRUE(f->sections.empty()); + EXPECT_TRUE(f->localSymbols.empty()); + EXPECT_TRUE(f->globalSymbols.empty()); + EXPECT_TRUE(f->undefinedSymbols.empty()); +} + + +TEST(ObjectFileYAML, roundTrip) { + std::string intermediate; + { + NormalizedFile f; + f.arch = lld::MachOLinkingContext::arch_x86_64; + f.fileType = llvm::MachO::MH_OBJECT; + f.flags = llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS; + f.os = lld::MachOLinkingContext::OS::macOSX; + toYAML(f, intermediate); + } + { + std::unique_ptr<NormalizedFile> f2 = fromYAML(intermediate); + EXPECT_EQ(f2->arch, lld::MachOLinkingContext::arch_x86_64); + EXPECT_EQ((int)(f2->fileType), llvm::MachO::MH_OBJECT); + EXPECT_EQ((int)(f2->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS); + EXPECT_TRUE(f2->sections.empty()); + EXPECT_TRUE(f2->localSymbols.empty()); + EXPECT_TRUE(f2->globalSymbols.empty()); + EXPECT_TRUE(f2->undefinedSymbols.empty()); + } +} + + +TEST(ObjectFileYAML, oneSymbol) { + std::unique_ptr<NormalizedFile> f = fromYAML( + "---\n" + "arch: x86_64\n" + "file-type: MH_OBJECT\n" + "global-symbols:\n" + " - name: _main\n" + " type: N_SECT\n" + " scope: [ N_EXT ]\n" + " sect: 1\n" + " desc: [ ]\n" + " value: 0x100\n" + "...\n"); + EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64); + EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT); + EXPECT_TRUE(f->sections.empty()); + EXPECT_TRUE(f->localSymbols.empty()); + EXPECT_TRUE(f->undefinedSymbols.empty()); + EXPECT_EQ(f->globalSymbols.size(), 1UL); + const Symbol& sym = f->globalSymbols[0]; + EXPECT_TRUE(sym.name.equals("_main")); + EXPECT_EQ((int)(sym.type), llvm::MachO::N_SECT); + EXPECT_EQ((int)(sym.scope), llvm::MachO::N_EXT); + EXPECT_EQ(sym.sect, 1); + EXPECT_EQ((int)(sym.desc), 0); + EXPECT_EQ((uint64_t)sym.value, 0x100ULL); +} + + +TEST(ObjectFileYAML, oneSection) { + std::unique_ptr<NormalizedFile> f = fromYAML( + "---\n" + "arch: x86_64\n" + "file-type: MH_OBJECT\n" + "sections:\n" + " - segment: __TEXT\n" + " section: __text\n" + " type: S_REGULAR\n" + " attributes: [ S_ATTR_PURE_INSTRUCTIONS ]\n" + " alignment: 1\n" + " address: 0x12345678\n" + " content: [ 0x90, 0x90 ]\n" + "...\n"); + EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64); + EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT); + EXPECT_TRUE(f->localSymbols.empty()); + EXPECT_TRUE(f->globalSymbols.empty()); + EXPECT_TRUE(f->undefinedSymbols.empty()); + EXPECT_EQ(f->sections.size(), 1UL); + const Section& sect = f->sections[0]; + EXPECT_TRUE(sect.segmentName.equals("__TEXT")); + EXPECT_TRUE(sect.sectionName.equals("__text")); + EXPECT_EQ((uint32_t)(sect.type), (uint32_t)(llvm::MachO::S_REGULAR)); + EXPECT_EQ((uint32_t)(sect.attributes), + (uint32_t)(llvm::MachO::S_ATTR_PURE_INSTRUCTIONS)); + EXPECT_EQ(sect.alignment, 1U); + EXPECT_EQ((uint64_t)sect.address, 0x12345678ULL); + EXPECT_EQ(sect.content.size(), 2UL); + EXPECT_EQ((int)(sect.content[0]), 0x90); + EXPECT_EQ((int)(sect.content[1]), 0x90); +} + + +TEST(ObjectFileYAML, hello_x86_64) { + std::unique_ptr<NormalizedFile> f = fromYAML( + "---\n" + "arch: x86_64\n" + "file-type: MH_OBJECT\n" + "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n" + "sections:\n" + " - segment: __TEXT\n" + " section: __text\n" + " type: S_REGULAR\n" + " attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS]\n" + " alignment: 0\n" + " address: 0x0000\n" + " content: [ 0x55, 0x48, 0x89, 0xe5, 0x48, 0x8d, 0x3d, 0x00,\n" + " 0x00, 0x00, 0x00, 0x30, 0xc0, 0xe8, 0x00, 0x00,\n" + " 0x00, 0x00, 0x31, 0xc0, 0x5d, 0xc3 ]\n" + " relocations:\n" + " - offset: 0x0e\n" + " type: X86_64_RELOC_BRANCH\n" + " length: 2\n" + " pc-rel: true\n" + " extern: true\n" + " symbol: 2\n" + " - offset: 0x07\n" + " type: X86_64_RELOC_SIGNED\n" + " length: 2\n" + " pc-rel: true\n" + " extern: true\n" + " symbol: 1\n" + " - segment: __TEXT\n" + " section: __cstring\n" + " type: S_CSTRING_LITERALS\n" + " attributes: [ ]\n" + " alignment: 0\n" + " address: 0x0016\n" + " content: [ 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x00 ]\n" + "global-symbols:\n" + " - name: _main\n" + " type: N_SECT\n" + " scope: [ N_EXT ]\n" + " sect: 1\n" + " value: 0x0\n" + "local-symbols:\n" + " - name: L_.str\n" + " type: N_SECT\n" + " scope: [ ]\n" + " sect: 2\n" + " value: 0x16\n" + "undefined-symbols:\n" + " - name: _printf\n" + " type: N_UNDF\n" + " value: 0x0\n" + "...\n"); + EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64); + EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT); + EXPECT_EQ((int)(f->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS); + EXPECT_EQ(f->sections.size(), 2UL); + + const Section& sect1 = f->sections[0]; + EXPECT_TRUE(sect1.segmentName.equals("__TEXT")); + EXPECT_TRUE(sect1.sectionName.equals("__text")); + EXPECT_EQ((uint32_t)(sect1.type), (uint32_t)(llvm::MachO::S_REGULAR)); + EXPECT_EQ((uint32_t)(sect1.attributes), + (uint32_t)(llvm::MachO::S_ATTR_PURE_INSTRUCTIONS + | llvm::MachO::S_ATTR_SOME_INSTRUCTIONS)); + EXPECT_EQ(sect1.alignment, 0U); + EXPECT_EQ((uint64_t)sect1.address, 0x0ULL); + EXPECT_EQ(sect1.content.size(), 22UL); + EXPECT_EQ((int)(sect1.content[0]), 0x55); + EXPECT_EQ((int)(sect1.content[1]), 0x48); + EXPECT_EQ(sect1.relocations.size(), 2UL); + const Relocation& reloc1 = sect1.relocations[0]; + EXPECT_EQ(reloc1.offset, 0x0eU); + EXPECT_FALSE(reloc1.scattered); + EXPECT_EQ((int)reloc1.type, (int)llvm::MachO::X86_64_RELOC_BRANCH); + EXPECT_EQ(reloc1.length, 2); + EXPECT_TRUE(reloc1.pcRel); + EXPECT_TRUE(reloc1.isExtern); + EXPECT_EQ(reloc1.symbol, 2U); + EXPECT_EQ((int)(reloc1.value), 0); + const Relocation& reloc2 = sect1.relocations[1]; + EXPECT_EQ(reloc2.offset, 0x07U); + EXPECT_FALSE(reloc2.scattered); + EXPECT_EQ((int)reloc2.type, (int)llvm::MachO::X86_64_RELOC_SIGNED); + EXPECT_EQ(reloc2.length, 2); + EXPECT_TRUE(reloc2.pcRel); + EXPECT_TRUE(reloc2.isExtern); + EXPECT_EQ(reloc2.symbol, 1U); + EXPECT_EQ((int)(reloc2.value), 0); + + const Section& sect2 = f->sections[1]; + EXPECT_TRUE(sect2.segmentName.equals("__TEXT")); + EXPECT_TRUE(sect2.sectionName.equals("__cstring")); + EXPECT_EQ((uint32_t)(sect2.type), (uint32_t)(llvm::MachO::S_CSTRING_LITERALS)); + EXPECT_EQ((uint32_t)(sect2.attributes), 0U); + EXPECT_EQ(sect2.alignment, 0U); + EXPECT_EQ((uint64_t)sect2.address, 0x016ULL); + EXPECT_EQ(sect2.content.size(), 7UL); + EXPECT_EQ((int)(sect2.content[0]), 0x68); + EXPECT_EQ((int)(sect2.content[1]), 0x65); + EXPECT_EQ((int)(sect2.content[2]), 0x6c); + + EXPECT_EQ(f->globalSymbols.size(), 1UL); + const Symbol& sym1 = f->globalSymbols[0]; + EXPECT_TRUE(sym1.name.equals("_main")); + EXPECT_EQ((int)(sym1.type), llvm::MachO::N_SECT); + EXPECT_EQ((int)(sym1.scope), llvm::MachO::N_EXT); + EXPECT_EQ(sym1.sect, 1); + EXPECT_EQ((int)(sym1.desc), 0); + EXPECT_EQ((uint64_t)sym1.value, 0x0ULL); + EXPECT_EQ(f->localSymbols.size(), 1UL); + const Symbol& sym2 = f->localSymbols[0]; + EXPECT_TRUE(sym2.name.equals("L_.str")); + EXPECT_EQ((int)(sym2.type), llvm::MachO::N_SECT); + EXPECT_EQ((int)(sym2.scope), 0); + EXPECT_EQ(sym2.sect, 2); + EXPECT_EQ((int)(sym2.desc), 0); + EXPECT_EQ((uint64_t)sym2.value, 0x16ULL); + EXPECT_EQ(f->undefinedSymbols.size(), 1UL); + const Symbol& sym3 = f->undefinedSymbols[0]; + EXPECT_TRUE(sym3.name.equals("_printf")); + EXPECT_EQ((int)(sym3.type), llvm::MachO::N_UNDF); + EXPECT_EQ((int)(sym3.scope), 0); + EXPECT_EQ(sym3.sect, 0); + EXPECT_EQ((int)(sym3.desc), 0); + EXPECT_EQ((uint64_t)sym3.value, 0x0ULL); +} + + +TEST(ObjectFileYAML, hello_x86) { + std::unique_ptr<NormalizedFile> f = fromYAML( + "---\n" + "arch: x86\n" + "file-type: MH_OBJECT\n" + "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n" + "sections:\n" + " - segment: __TEXT\n" + " section: __text\n" + " type: S_REGULAR\n" + " attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS]\n" + " alignment: 0\n" + " address: 0x0000\n" + " content: [ 0x55, 0x89, 0xe5, 0x83, 0xec, 0x08, 0xe8, 0x00,\n" + " 0x00, 0x00, 0x00, 0x58, 0x8d, 0x80, 0x16, 0x00,\n" + " 0x00, 0x00, 0x89, 0x04, 0x24, 0xe8, 0xe6, 0xff,\n" + " 0xff, 0xff, 0x31, 0xc0, 0x83, 0xc4, 0x08, 0x5d,\n" + " 0xc3 ]\n" + " relocations:\n" + " - offset: 0x16\n" + " type: GENERIC_RELOC_VANILLA\n" + " length: 2\n" + " pc-rel: true\n" + " extern: true\n" + " symbol: 1\n" + " - offset: 0x0e\n" + " scattered: true\n" + " type: GENERIC_RELOC_LOCAL_SECTDIFF\n" + " length: 2\n" + " pc-rel: false\n" + " value: 0x21\n" + " - offset: 0x0\n" + " scattered: true\n" + " type: GENERIC_RELOC_PAIR\n" + " length: 2\n" + " pc-rel: false\n" + " value: 0xb\n" + " - segment: __TEXT\n" + " section: __cstring\n" + " type: S_CSTRING_LITERALS\n" + " attributes: [ ]\n" + " alignment: 0\n" + " address: 0x0021\n" + " content: [ 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x00 ]\n" + "global-symbols:\n" + " - name: _main\n" + " type: N_SECT\n" + " scope: [ N_EXT ]\n" + " sect: 1\n" + " value: 0x0\n" + "undefined-symbols:\n" + " - name: _printf\n" + " type: N_UNDF\n" + " value: 0x0\n" + "...\n"); + EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86); + EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT); + EXPECT_EQ((int)(f->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS); + EXPECT_EQ(f->sections.size(), 2UL); + + const Section& sect1 = f->sections[0]; + EXPECT_TRUE(sect1.segmentName.equals("__TEXT")); + EXPECT_TRUE(sect1.sectionName.equals("__text")); + EXPECT_EQ((uint32_t)(sect1.type), (uint32_t)(llvm::MachO::S_REGULAR)); + EXPECT_EQ((uint32_t)(sect1.attributes), + (uint32_t)(llvm::MachO::S_ATTR_PURE_INSTRUCTIONS + | llvm::MachO::S_ATTR_SOME_INSTRUCTIONS)); + EXPECT_EQ(sect1.alignment, 0U); + EXPECT_EQ((uint64_t)sect1.address, 0x0ULL); + EXPECT_EQ(sect1.content.size(), 33UL); + EXPECT_EQ((int)(sect1.content[0]), 0x55); + EXPECT_EQ((int)(sect1.content[1]), 0x89); + EXPECT_EQ(sect1.relocations.size(), 3UL); + const Relocation& reloc1 = sect1.relocations[0]; + EXPECT_EQ(reloc1.offset, 0x16U); + EXPECT_FALSE(reloc1.scattered); + EXPECT_EQ((int)reloc1.type, (int)llvm::MachO::GENERIC_RELOC_VANILLA); + EXPECT_EQ(reloc1.length, 2); + EXPECT_TRUE(reloc1.pcRel); + EXPECT_TRUE(reloc1.isExtern); + EXPECT_EQ(reloc1.symbol, 1U); + EXPECT_EQ((int)(reloc1.value), 0); + const Relocation& reloc2 = sect1.relocations[1]; + EXPECT_EQ(reloc2.offset, 0x0eU); + EXPECT_TRUE(reloc2.scattered); + EXPECT_EQ((int)reloc2.type, (int)llvm::MachO::GENERIC_RELOC_LOCAL_SECTDIFF); + EXPECT_EQ(reloc2.length, 2); + EXPECT_FALSE(reloc2.pcRel); + EXPECT_EQ(reloc2.symbol, 0U); + EXPECT_EQ((int)(reloc2.value), 0x21); + const Relocation& reloc3 = sect1.relocations[2]; + EXPECT_EQ(reloc3.offset, 0U); + EXPECT_TRUE(reloc3.scattered); + EXPECT_EQ((int)reloc3.type, (int)llvm::MachO::GENERIC_RELOC_PAIR); + EXPECT_EQ(reloc3.length, 2); + EXPECT_FALSE(reloc3.pcRel); + EXPECT_EQ(reloc3.symbol, 0U); + EXPECT_EQ((int)(reloc3.value), 0xb); + + const Section& sect2 = f->sections[1]; + EXPECT_TRUE(sect2.segmentName.equals("__TEXT")); + EXPECT_TRUE(sect2.sectionName.equals("__cstring")); + EXPECT_EQ((uint32_t)(sect2.type), (uint32_t)(llvm::MachO::S_CSTRING_LITERALS)); + EXPECT_EQ((uint32_t)(sect2.attributes), 0U); + EXPECT_EQ(sect2.alignment, 0U); + EXPECT_EQ((uint64_t)sect2.address, 0x021ULL); + EXPECT_EQ(sect2.content.size(), 7UL); + EXPECT_EQ((int)(sect2.content[0]), 0x68); + EXPECT_EQ((int)(sect2.content[1]), 0x65); + EXPECT_EQ((int)(sect2.content[2]), 0x6c); + + EXPECT_EQ(f->globalSymbols.size(), 1UL); + const Symbol& sym1 = f->globalSymbols[0]; + EXPECT_TRUE(sym1.name.equals("_main")); + EXPECT_EQ((int)(sym1.type), llvm::MachO::N_SECT); + EXPECT_EQ((int)(sym1.scope), llvm::MachO::N_EXT); + EXPECT_EQ(sym1.sect, 1); + EXPECT_EQ((int)(sym1.desc), 0); + EXPECT_EQ((uint64_t)sym1.value, 0x0ULL); + EXPECT_EQ(f->undefinedSymbols.size(), 1UL); + const Symbol& sym2 = f->undefinedSymbols[0]; + EXPECT_TRUE(sym2.name.equals("_printf")); + EXPECT_EQ((int)(sym2.type), llvm::MachO::N_UNDF); + EXPECT_EQ((int)(sym2.scope), 0); + EXPECT_EQ(sym2.sect, 0); + EXPECT_EQ((int)(sym2.desc), 0); + EXPECT_EQ((uint64_t)sym2.value, 0x0ULL); +} + +TEST(ObjectFileYAML, hello_armv6) { + std::unique_ptr<NormalizedFile> f = fromYAML( + "---\n" + "arch: armv6\n" + "file-type: MH_OBJECT\n" + "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n" + "sections:\n" + " - segment: __TEXT\n" + " section: __text\n" + " type: S_REGULAR\n" + " attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS]\n" + " alignment: 2\n" + " address: 0x0000\n" + " content: [ 0x80, 0x40, 0x2d, 0xe9, 0x10, 0x00, 0x9f, 0xe5,\n" + " 0x0d, 0x70, 0xa0, 0xe1, 0x00, 0x00, 0x8f, 0xe0,\n" + " 0xfa, 0xff, 0xff, 0xeb, 0x00, 0x00, 0xa0, 0xe3,\n" + " 0x80, 0x80, 0xbd, 0xe8, 0x0c, 0x00, 0x00, 0x00 ]\n" + " relocations:\n" + " - offset: 0x1c\n" + " scattered: true\n" + " type: ARM_RELOC_SECTDIFF\n" + " length: 2\n" + " pc-rel: false\n" + " value: 0x20\n" + " - offset: 0x0\n" + " scattered: true\n" + " type: ARM_RELOC_PAIR\n" + " length: 2\n" + " pc-rel: false\n" + " value: 0xc\n" + " - offset: 0x10\n" + " type: ARM_RELOC_BR24\n" + " length: 2\n" + " pc-rel: true\n" + " extern: true\n" + " symbol: 1\n" + " - segment: __TEXT\n" + " section: __cstring\n" + " type: S_CSTRING_LITERALS\n" + " attributes: [ ]\n" + " alignment: 0\n" + " address: 0x0020\n" + " content: [ 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x00 ]\n" + "global-symbols:\n" + " - name: _main\n" + " type: N_SECT\n" + " scope: [ N_EXT ]\n" + " sect: 1\n" + " value: 0x0\n" + "undefined-symbols:\n" + " - name: _printf\n" + " type: N_UNDF\n" + " value: 0x0\n" + "...\n"); + EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv6); + EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT); + EXPECT_EQ((int)(f->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS); + EXPECT_EQ(f->sections.size(), 2UL); + + const Section& sect1 = f->sections[0]; + EXPECT_TRUE(sect1.segmentName.equals("__TEXT")); + EXPECT_TRUE(sect1.sectionName.equals("__text")); + EXPECT_EQ((uint32_t)(sect1.type), (uint32_t)(llvm::MachO::S_REGULAR)); + EXPECT_EQ((uint32_t)(sect1.attributes), + (uint32_t)(llvm::MachO::S_ATTR_PURE_INSTRUCTIONS + | llvm::MachO::S_ATTR_SOME_INSTRUCTIONS)); + EXPECT_EQ(sect1.alignment, 2U); + EXPECT_EQ((uint64_t)sect1.address, 0x0ULL); + EXPECT_EQ(sect1.content.size(), 32UL); + EXPECT_EQ((int)(sect1.content[0]), 0x80); + EXPECT_EQ((int)(sect1.content[1]), 0x40); + EXPECT_EQ(sect1.relocations.size(), 3UL); + const Relocation& reloc1 = sect1.relocations[0]; + EXPECT_EQ(reloc1.offset, 0x1cU); + EXPECT_TRUE(reloc1.scattered); + EXPECT_EQ((int)reloc1.type, (int)llvm::MachO::ARM_RELOC_SECTDIFF); + EXPECT_EQ(reloc1.length, 2); + EXPECT_FALSE(reloc1.pcRel); + EXPECT_EQ(reloc1.symbol, 0U); + EXPECT_EQ((int)(reloc1.value), 0x20); + const Relocation& reloc2 = sect1.relocations[1]; + EXPECT_EQ(reloc2.offset, 0x0U); + EXPECT_TRUE(reloc2.scattered); + EXPECT_EQ((int)reloc2.type, (int)llvm::MachO::ARM_RELOC_PAIR); + EXPECT_EQ(reloc2.length, 2); + EXPECT_FALSE(reloc2.pcRel); + EXPECT_EQ(reloc2.symbol, 0U); + EXPECT_EQ((int)(reloc2.value), 0xc); + const Relocation& reloc3 = sect1.relocations[2]; + EXPECT_EQ(reloc3.offset, 0x10U); + EXPECT_FALSE(reloc3.scattered); + EXPECT_EQ((int)reloc3.type, (int)llvm::MachO::ARM_RELOC_BR24); + EXPECT_EQ(reloc3.length, 2); + EXPECT_TRUE(reloc3.pcRel); + EXPECT_TRUE(reloc3.isExtern); + EXPECT_EQ(reloc3.symbol, 1U); + EXPECT_EQ((int)(reloc3.value), 0); + + const Section& sect2 = f->sections[1]; + EXPECT_TRUE(sect2.segmentName.equals("__TEXT")); + EXPECT_TRUE(sect2.sectionName.equals("__cstring")); + EXPECT_EQ((uint32_t)(sect2.type), (uint32_t)(llvm::MachO::S_CSTRING_LITERALS)); + EXPECT_EQ((uint32_t)(sect2.attributes), 0U); + EXPECT_EQ(sect2.alignment, 0U); + EXPECT_EQ((uint64_t)sect2.address, 0x020ULL); + EXPECT_EQ(sect2.content.size(), 7UL); + EXPECT_EQ((int)(sect2.content[0]), 0x68); + EXPECT_EQ((int)(sect2.content[1]), 0x65); + EXPECT_EQ((int)(sect2.content[2]), 0x6c); + + EXPECT_EQ(f->globalSymbols.size(), 1UL); + const Symbol& sym1 = f->globalSymbols[0]; + EXPECT_TRUE(sym1.name.equals("_main")); + EXPECT_EQ((int)(sym1.type), llvm::MachO::N_SECT); + EXPECT_EQ((int)(sym1.scope), llvm::MachO::N_EXT); + EXPECT_EQ(sym1.sect, 1); + EXPECT_EQ((int)(sym1.desc), 0); + EXPECT_EQ((uint64_t)sym1.value, 0x0ULL); + EXPECT_EQ(f->undefinedSymbols.size(), 1UL); + const Symbol& sym2 = f->undefinedSymbols[0]; + EXPECT_TRUE(sym2.name.equals("_printf")); + EXPECT_EQ((int)(sym2.type), llvm::MachO::N_UNDF); + EXPECT_EQ((int)(sym2.scope), 0); + EXPECT_EQ(sym2.sect, 0); + EXPECT_EQ((int)(sym2.desc), 0); + EXPECT_EQ((uint64_t)sym2.value, 0x0ULL); +} + + + +TEST(ObjectFileYAML, hello_armv7) { + std::unique_ptr<NormalizedFile> f = fromYAML( + "---\n" + "arch: armv7\n" + "file-type: MH_OBJECT\n" + "flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]\n" + "sections:\n" + " - segment: __TEXT\n" + " section: __text\n" + " type: S_REGULAR\n" + " attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS]\n" + " alignment: 1\n" + " address: 0x0000\n" + " content: [ 0x80, 0xb5, 0x40, 0xf2, 0x06, 0x00, 0x6f, 0x46,\n" + " 0xc0, 0xf2, 0x00, 0x00, 0x78, 0x44, 0xff, 0xf7,\n" + " 0xf8, 0xef, 0x00, 0x20, 0x80, 0xbd ]\n" + " relocations:\n" + " - offset: 0x0e\n" + " type: ARM_THUMB_RELOC_BR22\n" + " length: 2\n" + " pc-rel: true\n" + " extern: true\n" + " symbol: 1\n" + " - offset: 0x08\n" + " scattered: true\n" + " type: ARM_RELOC_HALF_SECTDIFF\n" + " length: 3\n" + " pc-rel: false\n" + " value: 0x16\n" + " - offset: 0x06\n" + " scattered: true\n" + " type: ARM_RELOC_PAIR\n" + " length: 3\n" + " pc-rel: false\n" + " value: 0xc\n" + " - offset: 0x02\n" + " scattered: true\n" + " type: ARM_RELOC_HALF_SECTDIFF\n" + " length: 2\n" + " pc-rel: false\n" + " value: 0x16\n" + " - offset: 0x0\n" + " scattered: true\n" + " type: ARM_RELOC_PAIR\n" + " length: 2\n" + " pc-rel: false\n" + " value: 0xc\n" + " - segment: __TEXT\n" + " section: __cstring\n" + " type: S_CSTRING_LITERALS\n" + " attributes: [ ]\n" + " alignment: 0\n" + " address: 0x0016\n" + " content: [ 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0a, 0x00 ]\n" + "global-symbols:\n" + " - name: _main\n" + " type: N_SECT\n" + " scope: [ N_EXT ]\n" + " sect: 1\n" + " desc: [ N_ARM_THUMB_DEF ]\n" + " value: 0x0\n" + "undefined-symbols:\n" + " - name: _printf\n" + " type: N_UNDF\n" + " value: 0x0\n" + "...\n"); + EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv7); + EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT); + EXPECT_EQ((int)(f->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS); + EXPECT_EQ(f->sections.size(), 2UL); + + const Section& sect1 = f->sections[0]; + EXPECT_TRUE(sect1.segmentName.equals("__TEXT")); + EXPECT_TRUE(sect1.sectionName.equals("__text")); + EXPECT_EQ((uint32_t)(sect1.type), (uint32_t)(llvm::MachO::S_REGULAR)); + EXPECT_EQ((uint32_t)(sect1.attributes), + (uint32_t)(llvm::MachO::S_ATTR_PURE_INSTRUCTIONS + | llvm::MachO::S_ATTR_SOME_INSTRUCTIONS)); + EXPECT_EQ(sect1.alignment, 1U); + EXPECT_EQ((uint64_t)sect1.address, 0x0ULL); + EXPECT_EQ(sect1.content.size(), 22UL); + EXPECT_EQ((int)(sect1.content[0]), 0x80); + EXPECT_EQ((int)(sect1.content[1]), 0xb5); + EXPECT_EQ(sect1.relocations.size(), 5UL); + const Relocation& reloc1 = sect1.relocations[0]; + EXPECT_EQ(reloc1.offset, 0x0eU); + EXPECT_FALSE(reloc1.scattered); + EXPECT_EQ((int)reloc1.type, (int)llvm::MachO::ARM_THUMB_RELOC_BR22); + EXPECT_EQ(reloc1.length, 2); + EXPECT_TRUE(reloc1.pcRel); + EXPECT_TRUE(reloc1.isExtern); + EXPECT_EQ(reloc1.symbol, 1U); + EXPECT_EQ((int)(reloc1.value), 0); + const Relocation& reloc2 = sect1.relocations[1]; + EXPECT_EQ(reloc2.offset, 0x8U); + EXPECT_TRUE(reloc2.scattered); + EXPECT_EQ((int)reloc2.type, (int)llvm::MachO::ARM_RELOC_HALF_SECTDIFF); + EXPECT_EQ(reloc2.length, 3); + EXPECT_FALSE(reloc2.pcRel); + EXPECT_EQ(reloc2.symbol, 0U); + EXPECT_EQ((int)(reloc2.value), 0x16); + const Relocation& reloc3 = sect1.relocations[2]; + EXPECT_EQ(reloc3.offset, 0x6U); + EXPECT_TRUE(reloc3.scattered); + EXPECT_EQ((int)reloc3.type, (int)llvm::MachO::ARM_RELOC_PAIR); + EXPECT_EQ(reloc3.length, 3); + EXPECT_FALSE(reloc3.pcRel); + EXPECT_EQ(reloc3.symbol, 0U); + EXPECT_EQ((int)(reloc3.value), 0xc); + const Relocation& reloc4 = sect1.relocations[3]; + EXPECT_EQ(reloc4.offset, 0x2U); + EXPECT_TRUE(reloc4.scattered); + EXPECT_EQ((int)reloc4.type, (int)llvm::MachO::ARM_RELOC_HALF_SECTDIFF); + EXPECT_EQ(reloc4.length, 2); + EXPECT_FALSE(reloc4.pcRel); + EXPECT_EQ(reloc4.symbol, 0U); + EXPECT_EQ((int)(reloc4.value), 0x16); + const Relocation& reloc5 = sect1.relocations[4]; + EXPECT_EQ(reloc5.offset, 0x0U); + EXPECT_TRUE(reloc5.scattered); + EXPECT_EQ((int)reloc5.type, (int)llvm::MachO::ARM_RELOC_PAIR); + EXPECT_EQ(reloc5.length, 2); + EXPECT_FALSE(reloc5.pcRel); + EXPECT_EQ(reloc5.symbol, 0U); + EXPECT_EQ((int)(reloc5.value), 0xc); + + const Section& sect2 = f->sections[1]; + EXPECT_TRUE(sect2.segmentName.equals("__TEXT")); + EXPECT_TRUE(sect2.sectionName.equals("__cstring")); + EXPECT_EQ((uint32_t)(sect2.type), (uint32_t)(llvm::MachO::S_CSTRING_LITERALS)); + EXPECT_EQ((uint32_t)(sect2.attributes), 0U); + EXPECT_EQ(sect2.alignment, 0U); + EXPECT_EQ((uint64_t)sect2.address, 0x016ULL); + EXPECT_EQ(sect2.content.size(), 7UL); + EXPECT_EQ((int)(sect2.content[0]), 0x68); + EXPECT_EQ((int)(sect2.content[1]), 0x65); + EXPECT_EQ((int)(sect2.content[2]), 0x6c); + + EXPECT_EQ(f->globalSymbols.size(), 1UL); + const Symbol& sym1 = f->globalSymbols[0]; + EXPECT_TRUE(sym1.name.equals("_main")); + EXPECT_EQ((int)(sym1.type), llvm::MachO::N_SECT); + EXPECT_EQ((int)(sym1.scope), llvm::MachO::N_EXT); + EXPECT_EQ(sym1.sect, 1); + EXPECT_EQ((int)(sym1.desc), (int)(llvm::MachO::N_ARM_THUMB_DEF)); + EXPECT_EQ((uint64_t)sym1.value, 0x0ULL); + EXPECT_EQ(f->undefinedSymbols.size(), 1UL); + const Symbol& sym2 = f->undefinedSymbols[0]; + EXPECT_TRUE(sym2.name.equals("_printf")); + EXPECT_EQ((int)(sym2.type), llvm::MachO::N_UNDF); + EXPECT_EQ((int)(sym2.scope), 0); + EXPECT_EQ(sym2.sect, 0); + EXPECT_EQ((int)(sym2.desc), 0); + EXPECT_EQ((uint64_t)sym2.value, 0x0ULL); +} diff --git a/unittests/MachOTests/empty_obj_x86_armv7.txt b/unittests/MachOTests/empty_obj_x86_armv7.txt new file mode 100644 index 0000000000000..9d340cb7132e7 --- /dev/null +++ b/unittests/MachOTests/empty_obj_x86_armv7.txt @@ -0,0 +1,1272 @@ +0xca, 0xfe, 0xba, 0xbe, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x07, 0x00, +0x00, 0x00, 0x03, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, +0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x40, +0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xcf, 0xfa, 0xed, 0xfe, 0x07, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, +0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, +0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x98, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, +0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x5f, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xce, 0xfa, 0xed, 0xfe, 0x0c, 0x00, 0x00, 0x00, 0x09, +0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7c, 0x00, +0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, +0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x74, +0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x5f, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 diff --git a/unittests/Makefile b/unittests/Makefile new file mode 100644 index 0000000000000..0fbb17d482898 --- /dev/null +++ b/unittests/Makefile @@ -0,0 +1,31 @@ +##===- unittests/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_UNITTEST_LEVEL := 1 +LLD_LEVEL := .. + +PARALLEL_DIRS = CoreTests DriverTests + +include $(LLD_LEVEL)/../../Makefile.config + +endif # LLD_LEVEL + +include $(LLD_LEVEL)/Makefile + +ifndef IS_UNITTEST_LEVEL + +MAKEFILE_UNITTEST_NO_INCLUDE_COMMON := 1 +include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest + +endif # IS_UNITTEST_LEVEL diff --git a/utils/astyle-options b/utils/astyle-options new file mode 100644 index 0000000000000..53c496bfebc0c --- /dev/null +++ b/utils/astyle-options @@ -0,0 +1,7 @@ +style=java +indent=spaces=2 +pad-oper +pad-header +unpad-paren +convert-tabs +align-pointer=name |