diff --git a/.remill_commit_id b/.remill_commit_id
index 892081d44..76e674a26 100644
--- a/.remill_commit_id
+++ b/.remill_commit_id
@@ -1 +1 @@
-86a6424d58cca6504a93bf7dc4363f14be9e216f
+5af012219674f2e1b0691748e6d62ad4d1817b86
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0995a80c1..20ec7940f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,16 +1,17 @@
-# Copyright (c) 2018 Trail of Bits, Inc.
+# Copyright (c) 2020 Trail of Bits, Inc.
#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
#
-# http://www.apache.org/licenses/LICENSE-2.0
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
project(mcsema)
cmake_minimum_required(VERSION 3.2)
@@ -81,19 +82,79 @@ set_source_files_properties(${PROJECT_PROTOBUFHEADERFILES} PROPERTIES
COMPILE_FLAGS "-Wno-sign-conversion -Wno-shorten-64-to-32 -Wno-conversion"
)
+# llvm
+find_package(LLVM REQUIRED CONFIG HINTS ${FINDPACKAGE_LLVM_HINTS})
+
+string(REPLACE "." ";" LLVM_VERSION_LIST ${LLVM_PACKAGE_VERSION})
+list(GET LLVM_VERSION_LIST 0 LLVM_MAJOR_VERSION)
+list(GET LLVM_VERSION_LIST 1 LLVM_MINOR_VERSION)
+set(REMILL_LLVM_VERSION "${LLVM_MAJOR_VERSION}.${LLVM_MINOR_VERSION}")
+
+#
+# target settings
+#
+
+set(MCSEMA_LIFT mcsema-lift-${REMILL_LLVM_VERSION})
+
+add_executable(${MCSEMA_LIFT}
+ ${PROJECT_PROTOBUFSOURCEFILES}
+
+ mcsema/Arch/Arch.cpp
+
+ mcsema/CFG/CFG.cpp
+
+ mcsema/BC/Callback.cpp
+ mcsema/BC/External.cpp
+ mcsema/BC/Function.cpp
+ mcsema/BC/Instruction.cpp
+ mcsema/BC/Legacy.cpp
+ mcsema/BC/Lift.cpp
+ mcsema/BC/Optimize.cpp
+ mcsema/BC/Segment.cpp
+ mcsema/BC/Util.cpp
+
+ tools/mcsema_lift/Lift.cpp
+)
+
+# Copy mcsema-disass in
+add_custom_command(
+ TARGET protobuf_python_module_ida POST_BUILD
+ DEPENDS {PROJECT_PROTOBUFPYTHONMODULE}
+ COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_PROTOBUFPYTHONMODULE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/mcsema_disass/ida7
+)
+
+# this is needed for the #include directives with absolutes paths to work correctly; it must
+# also be set to PUBLIC since mcsema-lift includes some files directly
+list(APPEND PROJECT_INCLUDEDIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR})
+
+add_dependencies(${MCSEMA_LIFT}
+ semantics
+ protobuf_python_module_ida
+ protobuf_python_module_binja)
+
+#
+# libraries
+#
+
# remill
if(NOT TARGET remill)
if("${PLATFORM_NAME}" STREQUAL "windows")
set(REMILL_FINDPACKAGE_HINTS HINTS "${CMAKE_INSTALL_PREFIX}/remill/lib")
endif()
+
find_package(remill REQUIRED ${REMILL_FINDPACKAGE_HINTS})
endif()
list(APPEND PROJECT_LIBRARIES remill)
-list(APPEND PROJECT_INCLUDEDIRECTORIES ${remill_INCLUDE_DIRS})
-# llvm
-find_package(LLVM REQUIRED CONFIG HINTS ${FINDPACKAGE_LLVM_HINTS})
+# anvill
+if(NOT TARGET anvill-${REMILL_LLVM_VERSION})
+ find_package(anvill REQUIRED)
+ if(NOT anvill_FOUND)
+ message(FATAL_ERROR "McSema depends upon Anvill being cloned into Remill's tools directory")
+ endif()
+endif()
+list(APPEND PROJECT_LIBRARIES anvill-${REMILL_LLVM_VERSION})
if (LLVM_Z3_INSTALL_DIR)
set(need_z3 TRUE)
@@ -111,9 +172,6 @@ if(need_z3)
endif()
endif()
-string(REPLACE "." ";" LLVM_VERSION_LIST ${LLVM_PACKAGE_VERSION})
-list(GET LLVM_VERSION_LIST 0 LLVM_MAJOR_VERSION)
-list(GET LLVM_VERSION_LIST 1 LLVM_MINOR_VERSION)
set(LLVM_LIBRARIES
LLVMCore LLVMSupport LLVMAnalysis LLVMipo LLVMIRReader
@@ -138,58 +196,6 @@ list(APPEND PROJECT_LIBRARIES glog::glog)
find_package(gflags REQUIRED)
list(APPEND PROJECT_LIBRARIES gflags)
-
-#
-# target settings
-#
-
-set(MCSEMA_LLVM_VERSION "${LLVM_MAJOR_VERSION}.${LLVM_MINOR_VERSION}")
-set(MCSEMA_LIFT mcsema-lift-${MCSEMA_LLVM_VERSION})
-
-add_executable(${MCSEMA_LIFT}
- ${PROJECT_PROTOBUFSOURCEFILES}
-
- mcsema/Arch/ABI.cpp
- mcsema/Arch/Arch.cpp
-
- mcsema/CFG/CFG.cpp
-
- mcsema/BC/Callback.cpp
- mcsema/BC/External.cpp
- mcsema/BC/Function.cpp
- mcsema/BC/Instruction.cpp
- mcsema/BC/Legacy.cpp
- mcsema/BC/Lift.cpp
- mcsema/BC/Optimize.cpp
- mcsema/BC/Segment.cpp
- mcsema/BC/Util.cpp
-
- tools/mcsema_lift/Lift.cpp
-)
-
-
-# Copy mcsema-disass in
-add_custom_command(
- TARGET protobuf_python_module_ida POST_BUILD
- DEPENDS {PROJECT_PROTOBUFPYTHONMODULE}
- COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_PROTOBUFPYTHONMODULE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/mcsema_disass/ida
-)
-
-add_custom_command(
- TARGET protobuf_python_module_binja POST_BUILD
- DEPENDS {PROJECT_PROTOBUFPYTHONMODULE}
- COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_PROTOBUFPYTHONMODULE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/mcsema_disass/binja
-)
-
-# this is needed for the #include directives with absolutes paths to work correctly; it must
-# also be set to PUBLIC since mcsema-lift includes some files directly
-list(APPEND PROJECT_INCLUDEDIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR})
-
-add_dependencies(${MCSEMA_LIFT}
- semantics protobuf_python_module_ida
- protobuf_python_module_binja
-)
-
#
# target settings
#
diff --git a/Dockerfile b/Dockerfile
index 7f645689e..baf3d409b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -23,18 +23,21 @@ RUN apt-get update && \
# Build-time dependencies go here
FROM trailofbits/cxx-common:llvm${LLVM_VERSION}-${DISTRO_BASE}-${ARCH} as deps
ARG LIBRARIES
-
RUN apt-get update && \
- apt-get install -qqy python2.7 libc6-dev wget liblzma-dev zlib1g-dev libtinfo-dev curl git build-essential ninja-build ccache && \
+ apt-get install -qqy python2.7 python3 python3-pip libc6-dev wget liblzma-dev zlib1g-dev libtinfo-dev curl git build-essential ninja-build libselinux1-dev libbsd-dev ccache && \
if [ "$(uname -m)" = "x86_64" ]; then dpkg --add-architecture i386 && apt-get update && apt-get install -qqy gcc-multilib g++-multilib zip zlib1g-dev:i386; fi && \
- rm -rf /var/lib/apt/lists/*
+ rm -rf /var/lib/apt/lists/* && \
+ pip3 install ccsyspath
# needed for 20.04 support until we migrate to py3
RUN curl https://bootstrap.pypa.io/get-pip.py --output get-pip.py && python2.7 get-pip.py
+RUN update-alternatives --install /usr/bin/python2 python2 /usr/bin/python2.7 1
+
WORKDIR /
COPY .remill_commit_id ./
RUN git clone https://github.com/lifting-bits/remill.git && \
+ git clone https://github.com/lifting-bits/anvill.git remill/tools/anvill && \
cd remill && \
echo "Using remill commit $(cat ../.remill_commit_id)" && \
git checkout $(cat ../.remill_commit_id)
diff --git a/LICENSE b/LICENSE
index 2bb9ad240..0ad25db4b 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,176 +1,661 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
- 1. Definitions.
+ Preamble
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
+ The precise terms and conditions for copying, distribution and
+modification follow.
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
+ TERMS AND CONDITIONS
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
+ 0. Definitions.
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
+ "This License" refers to version 3 of the GNU Affero General Public License.
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
+ 1. Source Code.
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
- END OF TERMS AND CONDITIONS
\ No newline at end of file
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+.
diff --git a/README.md b/README.md
index 762b15067..9e1139162 100644
--- a/README.md
+++ b/README.md
@@ -79,6 +79,8 @@ Why would anyone translate binaries *back* to bitcode?
| [Python](https://www.python.org/) | 2.7 |
| [Python Package Index](https://pypi.python.org/pypi) | Latest |
| [python-protobuf](https://pypi.python.org/pypi/protobuf) | 3.2.0 |
+| [python-clang](https://pypi.org/project/clang/) | 3.5.0 |
+| [ccsyspath](https://pypi.org/project/ccsyspath/) | 1.1.0 |
| [IDA Pro](https://www.hex-rays.com/products/ida) | 7.1+ |
| [Binary Ninja](https://binary.ninja/) | Latest |
| [Dyninst](https://www.dyninst.org/) | 9.3.2 |
@@ -87,16 +89,13 @@ Why would anyone translate binaries *back* to bitcode?
### Docker
-#### Step 1: Clone McSema
+#### Step 1: Download Dockerfile
-```sh
-git clone --depth 1 https://github.com/lifting-bits/mcsema.git
-cd mcsema
-```
+`wget https://raw.githubusercontent.com/lifting-bits/mcsema/master/tools/Dockerfile`
-#### Step 2: Add your disassembler (optional)
+#### Step 2: Add your disassembler
-Currently IDA, Binary Ninja, and Dyninst are supported for control-flow recovery, it's left as an exercise to the reader to install your disassembler of choice in a Dockerfile, but an example of installing Binary Ninja is provided (remember for Docker that paths need to be relative to where you built from):
+Currently IDA, Binary Ninja, and Dyninst are supported for control-flow recovery, it's left as an exercise to the reader to install your disassembler of choice, but an example of installing Binary Ninja is provided (remember for Docker that paths need to be relative to where you built from):
```
ADD local-relative/path/to/binaryninja/ /root/binaryninja/
ADD local-relative/path/to/.binaryninja/ /root/.binaryninja/ # <- Make sure there's no `lastrun` file
diff --git a/mcsema/Arch/ABI.cpp b/mcsema/Arch/ABI.cpp
deleted file mode 100644
index d84f6ec75..000000000
--- a/mcsema/Arch/ABI.cpp
+++ /dev/null
@@ -1,1248 +0,0 @@
-/*
- * Copyright (c) 2017 Trail of Bits, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include
-
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "mcsema/Arch/ABI.h"
-#include "mcsema/Arch/Arch.h"
-#include "mcsema/BC/Util.h"
-
-#include "remill/Arch/Arch.h"
-#include "remill/Arch/Name.h"
-#include "remill/BC/Util.h"
-#include "remill/BC/Version.h"
-#include "remill/OS/OS.h"
-
-namespace mcsema {
-
-enum ValueKind {
- kInvalidKind = 0,
- kI8 = (1 << 0),
- kI16 = (1 << 1),
- kI32 = (1 << 2),
- kI64 = (1 << 3),
- kF32 = (1 << 4),
- kF64 = (1 << 5),
- kF80 = (1 << 6),
- kVec = (1 << 7),
-
- kIntegralLeast32 = kI8 | kI16 | kI32,
- kIntegralLeast64 = kI8 | kI16 | kI32 | kI64,
-};
-
-namespace {
-
-static ValueKind KindOfValue(llvm::Type *type) {
- if (!type || type->isPointerTy()) {
- return (32 == gArch->address_size) ? kI32 : kI64;
-
- } else if (type->isVectorTy()) {
- return kVec;
- } else if (type->isIntegerTy()) {
- llvm::DataLayout dl(gModule.get());
- switch (dl.getTypeAllocSize(type)) {
- case 8: return kI64;
- case 4: return kI32;
- case 2: return kI16;
- case 1: return kI8;
- default:
- return kInvalidKind;
- }
- } else if (type->isX86_FP80Ty()) {
- return kF80;
- } else if (type->isDoubleTy()) {
- return kF64;
- } else if (type->isFloatTy()) {
- return kF32;
- } else {
- return kInvalidKind;
- }
-}
-
-static const char *StackPointerName(void) {
- static const char *sp_name = nullptr;
- if (sp_name) {
- return sp_name;
- }
-
- switch (gArch->arch_name) {
- case remill::kArchAArch64LittleEndian:
- sp_name = "SP";
- break;
-
- case remill::kArchX86:
- case remill::kArchX86_AVX:
- case remill::kArchX86_AVX512:
- sp_name = "ESP";
- break;
-
- case remill::kArchAMD64:
- case remill::kArchAMD64_AVX:
- case remill::kArchAMD64_AVX512:
- sp_name = "RSP";
- break;
- default:
- LOG(FATAL)
- << "Can't get stack pointer name for architecture: "
- << remill::GetArchName(gArch->arch_name);
- return nullptr;
- }
-
- return sp_name;
-}
-
-static const char *ThreadPointerNameX86(void) {
- switch (gArch->os_name) {
- case remill::kOSLinux:
- return "GS_BASE";
- case remill::kOSWindows:
- return "FS_BASE";
- default:
- return nullptr;
- }
-}
-
-static const char *ThreadPointerNameAMD64(void) {
- switch (gArch->os_name) {
- case remill::kOSLinux:
- return "FS_BASE";
- case remill::kOSWindows:
- return "GS_BASE";
- default:
- return nullptr;
- }
-}
-
-static const char *ThreadPointerName(void) {
- static const char *tp_name = nullptr;
- if (tp_name) {
- return tp_name;
- }
-
- switch (gArch->arch_name) {
- case remill::kArchAArch64LittleEndian:
- tp_name = "TPIDR_EL0";
- break;
-
- case remill::kArchX86:
- case remill::kArchX86_AVX:
- case remill::kArchX86_AVX512:
- tp_name = ThreadPointerNameX86();
- break;
-
- case remill::kArchAMD64:
- case remill::kArchAMD64_AVX:
- case remill::kArchAMD64_AVX512:
- tp_name = ThreadPointerNameAMD64();
- break;
-
- default:
- break;
- }
-
- LOG_IF(ERROR, !tp_name)
- << "Can't get thread pointer name for architecture "
- << remill::GetArchName(gArch->arch_name) << " and OS "
- << remill::GetOSName(gArch->os_name);
- return tp_name;
-}
-
-static uint64_t GetVectorRegSize(void) {
- switch (gArch->arch_name) {
- case remill::kArchAMD64:
- case remill::kArchX86:
- return 16;
- case remill::kArchAMD64_AVX:
- case remill::kArchX86_AVX:
- return 32;
- case remill::kArchAMD64_AVX512:
- case remill::kArchX86_AVX512:
- return 64;
- case remill::kArchAArch64LittleEndian:
- return 16;
- default:
- LOG(FATAL)
- << "Unknown vector register size for arch "
- << GetArchName(gArch->arch_name);
- return 0;
- }
-}
-
-struct VectorRegistersInfo {
- const char* base_name;
- size_t num_vec_regs;
-};
-
-static VectorRegistersInfo GetVectorRegisterInfo() {
- switch(gArch->arch_name) {
- case remill::kArchAArch64LittleEndian:
- return {"V", 32};
- case remill::kArchAMD64_AVX:
- return {"YMM", 16};
- case remill::kArchX86_AVX:
- return {"YMM", 8};
- case remill::kArchAMD64_AVX512:
- return {"ZMM", 32};
- case remill::kArchX86_AVX512:
- return {"ZMM", 8};
- default:
- return {"XMM", 8};
- }
-}
-
-// TODO(lukas): Test kVec flag for AArch64
-struct CallingConventionInfo {
- using ConstraintTable = std::vector;
-
- struct PackedReturnInfo {
- std::vector names;
- llvm::Type *unit_ptr_type;
- const ValueKind accepted_kinds;
- };
-
- // Sometimes types will get returned in several registers, even though
- // there is no indication of it in function signature
- static const PackedReturnInfo &GetPackedReturn(ValueKind val_kind) {
- static const PackedReturnInfo not_found = {{}, nullptr, kInvalidKind};
- auto ptr_size = static_cast(gArch->address_size);
-
- if (gArch->IsX86()) {
- static const std::vector return_info = {
- {{"EAX", "EDX"},
- llvm::Type::getIntNPtrTy(*gContext, ptr_size),
- kI64},
- };
- for (const auto &entry : return_info) {
- if (entry.accepted_kinds & val_kind) {
- return entry;
- }
- }
- }
- return not_found;
- }
-
- const ConstraintTable &GetArgumentTable(
- llvm::CallingConv::ID cc) const {
- for (const auto &t : arg_tables) {
- if (t.first == cc) {
- return t.second;
- }
- }
- LOG(FATAL)
- << "Unknown ABI/calling convention: " << cc;
- return gInvalidTable;
- }
-
- const ConstraintTable &GetReturnTable(
- llvm::CallingConv::ID cc) const {
- for (const auto &t : ret_tables) {
- if (t.first == cc) {
- return t.second;
- }
- }
- LOG(FATAL)
- << "Unknown ABI/calling convention: " << cc;
- return gInvalidTable;
- }
-
- static const CallingConventionInfo &Instance(void) {
- static CallingConventionInfo instance;
- return instance;
- }
-
- CallingConventionInfo(const CallingConventionInfo &) = delete;
- void operator=(const CallingConventionInfo &) = delete;
-
- private:
- CallingConventionInfo(void) {
- CreateArgumentsConstraintTables();
- CreateReturnConstraintTable();
- }
-
- void CreateArgumentsConstraintTables(void) {
- const auto &vector_reg_info = GetVectorRegisterInfo();
- const auto &vector_base_name = vector_reg_info.base_name;
- if (gArch->IsAMD64()) {
- std::vector amd64_sysv_args = {
- {"RDI", kIntegralLeast64},
- {"RSI", kIntegralLeast64},
- {"RDX", kIntegralLeast64},
- {"RCX", kIntegralLeast64},
- {"R8", kIntegralLeast64},
- {"R9", kIntegralLeast64}
- };
-
- for (unsigned i = 0; i < 8; ++i) {
- auto name = vector_base_name + std::to_string(i);
- amd64_sysv_args.push_back({name, kF32 | kF64 | kVec});
- }
- arg_tables.emplace_back(llvm::CallingConv::X86_64_SysV,
- std::move(amd64_sysv_args));
-
- std::vector amd64_win64_args = {
- {"RCX", kIntegralLeast64},
- {"RDX", kIntegralLeast64},
- {"R8", kIntegralLeast64},
- {"R9", kIntegralLeast64}
- };
-
- for (auto i = 0U; i < 4; ++i) {
- auto name = vector_base_name + std::to_string(i);
- amd64_win64_args.push_back({name, kF32 | kF64 | kVec});
- }
- arg_tables.emplace_back(llvm::CallingConv::Win64,
- std::move(amd64_win64_args));
- } else if (gArch->IsX86()) {
- std::vector x86_fast_call_args = {
- {"ECX", kIntegralLeast32},
- {"EDX", kIntegralLeast32},
- };
- arg_tables.emplace_back(llvm::CallingConv::X86_FastCall,
- std::move(x86_fast_call_args));
-
- std::vector x86_this_call_args = {
- {"ECX", kIntegralLeast32},
- };
- arg_tables.emplace_back(llvm::CallingConv::X86_ThisCall,
- std::move(x86_this_call_args));
-
- // stdcall takes all args on the stack.
- arg_tables.emplace_back(llvm::CallingConv::X86_StdCall,
- ConstraintTable{});
-
- // cdecl takes all args on the stack.
- arg_tables.emplace_back(llvm::CallingConv::C,
- ConstraintTable{});
- } else if (gArch->IsAArch64()) {
- std::vector aarch64_args = {
- {"X0", kIntegralLeast64},
- {"X1", kIntegralLeast64},
- {"X2", kIntegralLeast64},
- {"X3", kIntegralLeast64},
- {"X4", kIntegralLeast64},
- {"X5", kIntegralLeast64},
- {"X6", kIntegralLeast64},
- {"X7", kIntegralLeast64},
-
- {"D0", kF32 | kF64},
- {"D1", kF32 | kF64},
- {"D2", kF32 | kF64},
- {"D3", kF32 | kF64},
- {"D4", kF32 | kF64},
- {"D5", kF32 | kF64},
- {"D6", kF32 | kF64},
- {"D7", kF32 | kF64},
- {"D8", kF32 | kF64},
- {"D9", kF32 | kF64},
- {"D10", kF32 | kF64},
- {"D11", kF32 | kF64},
- {"D12", kF32 | kF64},
- {"D13", kF32 | kF64},
- {"D14", kF32 | kF64},
- {"D15", kF32 | kF64},
- {"D16", kF32 | kF64},
- {"D17", kF32 | kF64},
- {"D18", kF32 | kF64},
- {"D19", kF32 | kF64},
- {"D20", kF32 | kF64},
- {"D21", kF32 | kF64},
- {"D22", kF32 | kF64},
- {"D23", kF32 | kF64},
- {"D24", kF32 | kF64},
- {"D25", kF32 | kF64},
- {"D26", kF32 | kF64},
- {"D27", kF32 | kF64},
- {"D28", kF32 | kF64},
- {"D29", kF32 | kF64},
- {"D30", kF32 | kF64},
- {"D31", kF32 | kF64},
- };
-
- for(auto i = 0U; i < 8; ++i) {
- auto vec_reg_name = vector_base_name + std::to_string(i);
- aarch64_args.push_back({vec_reg_name, kVec});
- }
- arg_tables.emplace_back(llvm::CallingConv::C,
- std::move(aarch64_args));
- }
- }
-
- void CreateReturnConstraintTable(void) {
- const auto &vector_reg_info = GetVectorRegisterInfo();
- size_t size = vector_reg_info.num_vec_regs;
- const auto &vector_base_name = vector_reg_info.base_name;
-
- if (gArch->IsAMD64()) {
- ConstraintTable sysv64_table = {
- {"RAX", kIntegralLeast64},
- {"RDX", kIntegralLeast64}
- };
-
- for (auto i = 0U; i < size; ++i) {
- auto name = vector_base_name + std::to_string(i);
- sysv64_table.push_back({name, kF32 | kF64 | kVec});
- }
- sysv64_table.push_back({"ST0", kF80});
- sysv64_table.push_back({"ST1", kF80});
- ret_tables.emplace_back(llvm::CallingConv::X86_64_SysV,
- std::move(sysv64_table));
-
- ConstraintTable win64_table = {{"RAX", kIntegralLeast64}};
- for (auto i = 0U; i < size; ++i) {
- auto name = vector_base_name + std::to_string(i);
- win64_table.push_back({name, kF32 | kF64 | kVec});
- }
- win64_table.push_back({"ST0", kF80});
- ret_tables.emplace_back(llvm::CallingConv::Win64,
- std::move(win64_table));
- } else if (gArch->IsX86()) {
- ConstraintTable x86_table = {
- {"EAX", kIntegralLeast32},
- {"ST0", kF80},
- };
- ret_tables.emplace_back(llvm::CallingConv::X86_StdCall,
- x86_table);
- ret_tables.emplace_back(llvm::CallingConv::X86_FastCall,
- x86_table);
- ret_tables.emplace_back(llvm::CallingConv::X86_ThisCall,
- std::move(x86_table));
-
- ConstraintTable cdecl_table = {
- {"EAX", kIntegralLeast32 | kF32},
- {"ST0", kF80},
- };
- ret_tables.emplace_back(llvm::CallingConv::C,
- std::move(cdecl_table));
-
- } else if (gArch->IsAArch64()) {
- ConstraintTable AArch64_table = {
- {"X0", kIntegralLeast64},
- {"D0", kF64},
- {"S0", kF32},
- };
- ret_tables.emplace_back(llvm::CallingConv::C,
- std::move(AArch64_table));
- }
- }
-
- std::vector> arg_tables;
- std::vector> ret_tables;
-
- static const ConstraintTable gInvalidTable;
-};
-
-const CallingConventionInfo::ConstraintTable
-CallingConventionInfo::gInvalidTable;
-
-static uint64_t DefaultUsedStackBytes(llvm::CallingConv::ID cc) {
- switch (cc) {
- case llvm::CallingConv::X86_64_SysV:
- return 8; // Size of return address on the stack.
-
- case llvm::CallingConv::Win64:
- return 8 + 32; // Return address + shadow space.
-
- case llvm::CallingConv::X86_FastCall:
- case llvm::CallingConv::X86_StdCall:
- case llvm::CallingConv::X86_ThisCall:
- return 4; // Size of return address on the stack.
-
- default:
- return 0;
- }
-}
-
-static llvm::Type* RetrieveArgumentType(llvm::Type *original_type, unsigned index) {
- if (original_type->isPointerTy()) {
- return original_type;
- }
- if (auto struct_type = llvm::dyn_cast(original_type)) {
- return struct_type->getElementType(index);
- } else {
- return original_type;
- }
-}
-
-// llvm::CompositeType as common parent does not provide getNumElements
-static uint64_t GetNumberOfElements(llvm::Type* original_type) {
- if (auto struct_type = llvm::dyn_cast(original_type)) {
- return struct_type->getNumElements();
- } else {
- return 1;
- }
-}
-
-static void ExtractFromVector(llvm::BasicBlock *block,
- llvm::Value *ret_val,
- llvm::Value *reg_ptr,
- size_t count,
- size_t start=0) {
-
- llvm::IRBuilder<> ir(block);
-
- for (size_t i = 0; i < count; ++i) {
- auto offset = ir.CreateGEP(reg_ptr, GetConstantInt(64, i));
- auto extract = ir.CreateExtractElement(ret_val,
- GetConstantInt(64, i + start));
- ir.CreateStore(extract, offset);
- }
-}
-
-static llvm::Value *InsertIntoVector(llvm::BasicBlock *block,
- llvm::Value *base_value,
- llvm::Value *reg_ptr,
- size_t count,
- size_t start=0) {
-
- llvm::IRBuilder<> ir(block);
- for (size_t i = 0; i < count; ++i) {
- auto offset = ir.CreateGEP(reg_ptr, GetConstantInt(64, i));
- auto load = ir.CreateLoad(offset);
- base_value = ir.CreateInsertElement(
- base_value,
- load,
- GetConstantInt(64, i + start));
- }
- return base_value;
-}
-
-// Scan through the register table. If we can match this argument request
-// to a register then do so.
-static const char *GetVarImpl(
- ValueKind val_kind,
- const std::vector &table,
- uint64_t &bitmap) {
-
- for (auto i = 0U; i < table.size(); ++i) {
- const auto ®_loc = table[i];
- if (val_kind == (reg_loc.accepted_val_kinds & val_kind)) {
- auto mask = 1ULL << i;
- if (!(bitmap & mask)) {
- bitmap |= mask;
- return reg_loc.var_name.c_str();
- }
- }
- }
- return nullptr;
-}
-
-// In special cases one type can be returned via two registers
-static bool TryStorePackedType(llvm::BasicBlock *block, llvm::Value *ret_val) {
- const auto ®s = CallingConventionInfo::GetPackedReturn(
- KindOfValue(ret_val->getType()));
-
- if (regs.names.empty() || regs.accepted_kinds == kInvalidKind) {
- LOG(INFO)
- << "Could not store return type "
- << remill::LLVMThingToString(ret_val->getType())
- << " as packed type";
- return false;
- }
-
- llvm::IRBuilder<> ir(block);
-
- // Need to get ptr to some type smaller than actual type is, as
- // it will be split into multiple registers
- auto ret_val_alloca = ir.CreateAlloca(ret_val->getType());
- ir.CreateStore(ret_val, ret_val_alloca);
- auto ptr_ret_val = ir.CreateBitCast(ret_val_alloca, regs.unit_ptr_type);
-
- for (auto i = 0U; i < regs.names.size(); ++i) {
- // Get partial value from offset
- auto partial_value_ptr = ir.CreateGEP(ptr_ret_val, GetConstantInt(64, i));
- auto partial_value = ir.CreateLoad(partial_value_ptr);
-
- llvm::Value *dest_loc = remill::FindVarInFunction(block, regs.names[i]);
-
- // Store actual value
- dest_loc = ir.CreateBitCast(dest_loc,
- regs.unit_ptr_type);
- ir.CreateStore(partial_value, dest_loc);
- }
- return true;
-}
-
-} // namespace
-
-CallingConvention::CallingConvention(llvm::CallingConv::ID cc_)
- : cc(cc_),
- used_reg_bitmap(0),
- used_return_bitmap(0),
- num_loaded_stack_bytes(DefaultUsedStackBytes(cc)),
- num_stored_stack_bytes(0),
- sp_name(StackPointerName()),
- tp_name(ThreadPointerName()),
- reg_table(CallingConventionInfo::Instance().GetArgumentTable(cc)),
- return_table(CallingConventionInfo::Instance().GetReturnTable(cc)) {}
-
-// TODO(lukas): Test win64 calling convention
-const char *CallingConvention::GetVarForNextArgument(llvm::Type *val_type) {
- auto val_kind = KindOfValue(val_type);
- auto next_var = GetVarImpl(val_kind, reg_table, used_reg_bitmap);
-
- // Win64 calling convention chooses one from pair
- // {gpr, xmm} and leaves the second one empty
- //
- // For example call of function foo(int32, float, int32)
- // will fill registers %rcx, %xmm1, %r8
- if (llvm::CallingConv::Win64 == cc) {
- val_kind = (val_kind == kIntegralLeast64) ? kF64 : kIntegralLeast64;
- GetVarImpl(val_kind, reg_table, used_reg_bitmap);
- }
- return next_var;
-}
-
-const char *CallingConvention::GetVarForNextReturn(llvm::Type *val_type) {
- return GetVarImpl(KindOfValue(val_type), return_table, used_return_bitmap);
-}
-
-static llvm::Function *ReadIntFromMemFunc(uint64_t size_bytes) {
- if (8 == size_bytes) {
- return gModule->getFunction("__remill_read_memory_64");
- } else if (4 == size_bytes) {
- return gModule->getFunction("__remill_read_memory_32");
- } else if (2 == size_bytes) {
- return gModule->getFunction("__remill_read_memory_16");
- } else if (1 == size_bytes) {
- return gModule->getFunction("__remill_read_memory_8");
- } else {
- LOG(FATAL)
- << "Cannot find function to read " << size_bytes
- << "-byte integer from memory.";
- return nullptr;
- }
-}
-
-static llvm::Function *WriteIntToMemFunc(uint64_t size_bytes) {
- if (8 == size_bytes) {
- return gModule->getFunction("__remill_write_memory_64");
- } else if (4 == size_bytes) {
- return gModule->getFunction("__remill_write_memory_32");
- } else if (2 == size_bytes) {
- return gModule->getFunction("__remill_write_memory_16");
- } else if (1 == size_bytes) {
- return gModule->getFunction("__remill_write_memory_8");
- } else {
- LOG(FATAL)
- << "Cannot find function to read " << size_bytes
- << "-byte integer from memory.";
- return nullptr;
- }
-}
-
-// In llvm types that are expected to go into vector registers will
-// be of vector type, for example: <2 x float> will go into one %xmm.
-// External library must be compiled for the same architecture that
-// is passed to mcsema-lift, otherwise types may be wrong.
-llvm::Value *CallingConvention::LoadVectorArgument(
- llvm::BasicBlock *block,
- llvm::VectorType *goal_type) {
-
- llvm::IRBuilder<> ir(block);
- llvm::Value *base_value = llvm::Constant::getNullValue(goal_type);
-
- llvm::Type *under_type = goal_type->getElementType();
- llvm::DataLayout dl(gModule.get());
- const size_t num_elements = goal_type->getNumElements();
- const ssize_t element_size = static_cast(
- dl.getTypeAllocSize(under_type));
- const ssize_t reg_size = static_cast(GetVectorRegSize());
- const ssize_t reg_element_capacity = reg_size / element_size;
- ssize_t remaining = static_cast(num_elements);
-
- for (ssize_t i = 0; remaining > 0; ++i, remaining -= reg_size) {
- auto reg_var_name = GetVarForNextArgument(goal_type);
- LOG_IF(FATAL, !reg_var_name)
- << "Could not find available vector register";
-
- llvm::Value *dest_loc = remill::FindVarInFunction(block, reg_var_name);
- dest_loc = ir.CreateBitCast(dest_loc,
- llvm::PointerType::get(under_type, 0));
-
- const auto count = std::min(reg_element_capacity, remaining);
- base_value = InsertIntoVector(
- block, base_value, dest_loc,
- static_cast(count),
- static_cast(i * reg_element_capacity));
- }
- return base_value;
-}
-
-
-llvm::Value *CallingConvention::LoadNextSimpleArgument(
- llvm::BasicBlock *block,
- llvm::Type *goal_type) {
- if (!goal_type) {
- goal_type = gWordType;
- }
-
- llvm::IRBuilder<> ir(block);
-
- if (auto vector_type = llvm::dyn_cast(goal_type)) {
- return LoadVectorArgument(block, vector_type);
- }
-
- if (auto reg_var_name = GetVarForNextArgument(goal_type)) {
- auto reg_ptr = remill::FindVarInFunction(block, reg_var_name);
- return ir.CreateLoad(
- ir.CreateBitCast(reg_ptr, llvm::PointerType::get(goal_type, 0)));
- }
-
- // We can't match the argument request to a register, so lets look for it on
- // the stack. The supported calling conventions are sane, to the extent
- // that they push arguments onto the stack in reverse order (i.e. last arg
- // first).
- auto sp = LoadStackPointer(block);
- CHECK(sp->getType() == gWordType);
-
- auto addr_size = static_cast(gArch->address_size / 8U);
- auto offset = llvm::ConstantInt::get(gWordType, num_loaded_stack_bytes);
-
- auto addr = ir.CreateAdd(sp, offset);
- std::vector args = {remill::LoadMemoryPointer(block), addr};
-
- llvm::DataLayout dl(gModule.get());
- auto alloc_size = static_cast(dl.getTypeAllocSize(goal_type));
-
- llvm::Value *val = nullptr;
-
- if (goal_type->isX86_FP80Ty()) {
- val = ir.CreateFPExt(
- ir.CreateCall(gModule->getFunction("__remill_read_memory_f80"), args),
- llvm::Type::getX86_FP80Ty(*gContext));
-
- } else if (goal_type->isDoubleTy()) {
- val = ir.CreateCall(gModule->getFunction("__remill_read_memory_f64"), args);
-
- } else if (goal_type->isFloatTy()) {
- val = ir.CreateCall(gModule->getFunction("__remill_read_memory_f32"), args);
-
- } else if (goal_type->isIntegerTy()) {
- auto read_mem = ReadIntFromMemFunc(alloc_size);
- val = ir.CreateCall(read_mem, args);
- if (dl.getTypeSizeInBits(goal_type) <
- dl.getTypeAllocSizeInBits(goal_type)) {
- val = ir.CreateTrunc(val, goal_type);
- }
-
- } else if (goal_type->isPointerTy()) {
- llvm::Function *func = nullptr;
- if (32 == gArch->address_size) {
- func = gModule->getFunction("__remill_read_memory_32");
- } else {
- func = gModule->getFunction("__remill_read_memory_64");
- }
- val = ir.CreateIntToPtr(ir.CreateCall(func, args), goal_type);
-
- } else {
- LOG(FATAL)
- << "Can't handle reading an " << remill::LLVMThingToString(goal_type)
- << " value from the stack";
- }
-
- // Bump the stack pointer.
- alloc_size = std::max(alloc_size, addr_size);
- num_loaded_stack_bytes += alloc_size;
- return val;
-}
-
-llvm::Value *CallingConvention::LoadNextArgument(llvm::BasicBlock *block,
- llvm::Type *target_type,
- bool is_byval) {
- if (!target_type) {
- target_type = gWordType;
- }
-
- llvm::IRBuilder<> ir(block);
-
- std::vector underlying_values;
- llvm::Type *goal_type = target_type;
-
- if (target_type->isPointerTy() && !is_byval) {
- return LoadNextSimpleArgument(block, target_type);
- }
-
- if (auto struct_type = llvm::dyn_cast(goal_type)) {
- std::vector underlying_values;
- for (unsigned i = 0; i < struct_type->getNumElements(); ++i) {
- llvm::Type *under_type = struct_type->getElementType(i);
- underlying_values.push_back(
- LoadNextSimpleArgument(block, under_type));
- }
-
- llvm::IRBuilder<> ir(block);
- auto alloc_struct = ir.CreateAlloca(target_type);
- for (unsigned i = 0; i < underlying_values.size(); ++i) {
- llvm::Value *offsets[] = {GetConstantInt(64, 0), GetConstantInt(64, i)};
- auto gep = ir.CreateGEP(alloc_struct, offsets);
- ir.CreateStore(underlying_values[i], gep);
- }
- return ir.CreateLoad(alloc_struct);
-
- } else if (is_byval) {
- // byval attribute says that caller makes a copy of argument on the stack
- // this happens if type of argument is bigger than 128 bits.
- auto stack_ptr = LoadStackPointer(block);
- auto offset = llvm::ConstantInt::get(gWordType, num_loaded_stack_bytes);
- auto addr = ir.CreateAdd(stack_ptr, offset);
-
- llvm::DataLayout dl(gModule.get());
- auto ptr_type = llvm::dyn_cast(target_type);
-
- num_loaded_stack_bytes += dl.getTypeAllocSize(ptr_type->getElementType());
- return ir.CreateIntToPtr(addr, target_type);
- }
-
- return LoadNextSimpleArgument(block, target_type);
-}
-
-void CallingConvention::StoreVectorRetValue(llvm::BasicBlock *block,
- llvm::Value *ret_val,
- llvm::VectorType *goal_type) {
- llvm::IRBuilder<> ir(block);
- llvm::Type *under_type = goal_type->getElementType();
- llvm::DataLayout dl(gModule.get());
-
- const size_t num_elements = goal_type->getNumElements();
- const ssize_t reg_size = static_cast(GetVectorRegSize());
- const ssize_t element_size = static_cast(
- dl.getTypeAllocSize(under_type));
- const ssize_t reg_element_capacity = reg_size / element_size;
- ssize_t remaining = static_cast(num_elements);
-
- for (ssize_t i = 0U; remaining > 0; ++i, remaining -= reg_element_capacity) {
- auto reg_var_name = GetVarForNextReturn(goal_type);
- LOG_IF(FATAL, !reg_var_name)
- << "Could not find available vector register";
- llvm::Value *dest_loc = remill::FindVarInFunction(block, reg_var_name);
-
- // Clear out whatever was already there
- auto storage_type = llvm::dyn_cast(
- dest_loc->getType())->getElementType();
- ir.CreateStore(llvm::Constant::getNullValue(storage_type), dest_loc);
-
- dest_loc = ir.CreateBitCast(dest_loc,
- llvm::PointerType::get(under_type, 0));
-
- const auto count = std::min(reg_element_capacity, remaining);
- const auto already_done = i * reg_element_capacity;
- ExtractFromVector(block, ret_val, dest_loc,
- static_cast(count),
- static_cast(already_done));
- }
-}
-
-void CallingConvention::StoreReturnValue(llvm::BasicBlock *block,
- llvm::Value *ret_val) {
- if (!ret_val) {
- return;
- }
-
- auto val_type = ret_val->getType();
- if (val_type->isVoidTy()) {
- return;
- }
-
- llvm::IRBuilder<> ir(block);
-
- for (unsigned i = 0; i < GetNumberOfElements(val_type); ++i) {
- llvm::Value* target_val = ret_val;
-
- if (val_type->isStructTy()) {
- target_val = ir.CreateExtractValue(ret_val, i);
- }
- auto under_type = RetrieveArgumentType(val_type, i);
-
- if (auto vector_type = llvm::dyn_cast(under_type)) {
- StoreVectorRetValue(block, target_val, vector_type);
- continue;
- }
-
- auto val_var = GetVarForNextReturn(under_type);
-
- // If register was not found in default table it's possible
- // that we are dealing with some special corner case
- if (!val_var) {
- if (GetNumberOfElements(val_type) == 1 &&
- TryStorePackedType(block, ret_val)) {
- return;
- }
-
- LOG(FATAL)
- << "Cannot decide how to store "
- << remill::LLVMThingToString(under_type)
- << " part of " << remill::LLVMThingToString(val_type);
- }
-
- // If it's a pointer then convert it to a pointer-sized integer.
- if (under_type->isPointerTy()) {
- target_val = ir.CreatePtrToInt(target_val, gWordType);
- under_type = gWordType;
- }
-
- // If it's an 80-bit float then convert it to a double.
- if (under_type->isX86_FP80Ty()) {
- under_type = llvm::Type::getDoubleTy(*gContext);
- target_val = ir.CreateFPTrunc(target_val, under_type);
- }
-
- CHECK(under_type->isIntegerTy() || under_type->isFloatTy() ||
- under_type->isDoubleTy());
-
- llvm::DataLayout dl(gModule.get());
-
- // Canonicalize integer return values into address-sized values.
- if (under_type->isIntegerTy()) {
- auto size = dl.getTypeSizeInBits(under_type);
- if (size < gArch->address_size) {
- under_type = gWordType;
- target_val = ir.CreateZExt(target_val, under_type);
-
- } else if (size > gArch->address_size) {
- LOG(ERROR)
- << "Truncating value of type "
- << remill::LLVMThingToString(under_type)
- << " to store it into variable " << val_var
- << " of type " << remill::LLVMThingToString(gWordType);
- target_val = ir.CreateTrunc(target_val, gWordType);
- under_type = gWordType;
- }
-
- // Storing a `float` into an x87 register, convert it to a `double`.
- } else if (under_type->isFloatTy()) {
- if (val_var && val_var[0] == 'S' && val_var[1] == 'T') {
- under_type = llvm::Type::getDoubleTy(*gContext);
- target_val = ir.CreateFPExt(target_val, under_type);
- }
- }
-
- llvm::Value *dest_loc = remill::FindVarInFunction(block, val_var);
-
- // Clear out whatever was already there.
- auto storage_type = llvm::dyn_cast(
- dest_loc->getType())->getElementType();
- ir.CreateStore(llvm::Constant::getNullValue(storage_type), dest_loc);
-
- // Add in the new value.
- dest_loc = ir.CreateBitCast(dest_loc, llvm::PointerType::get(under_type, 0));
- ir.CreateStore(target_val, dest_loc);
- }
-
-}
-
-void CallingConvention::StoreArguments(
- llvm::BasicBlock *block, const std::vector &arg_vals) {
-
- auto memory_ref = remill::LoadMemoryPointerRef(block);
-
- llvm::IRBuilder<> ir(block);
- std::vector stack_arg_vals;
-
- // First try to put as many as possible into registers.
- for (auto arg_val : arg_vals) {
- auto arg_type = arg_val->getType();
- if (auto reg_var_name = GetVarForNextArgument(arg_type)) {
- auto reg_ptr = remill::FindVarInFunction(block, reg_var_name);
- ir.CreateStore(
- arg_val,
- ir.CreateBitCast(reg_ptr, llvm::PointerType::get(arg_type, 0)));
- } else {
- stack_arg_vals.push_back(arg_val);
- }
- }
-
- // Now we have some left that need to be pushed onto the stack. We're going
- // to push them onto the stack in reverse order.
- CHECK(gArch->IsX86() || gArch->IsAMD64() || gArch->IsAArch64());
- std::reverse(stack_arg_vals.begin(), stack_arg_vals.end());
-
- auto addr_size = static_cast(gArch->address_size / 8U);
- auto sp = LoadStackPointer(block);
- llvm::Value *memory = ir.CreateLoad(memory_ref);
-
- llvm::DataLayout dl(gModule.get());
-
- std::vector args(3, nullptr);
-
- for (auto arg_val : stack_arg_vals) {
- auto arg_type = arg_val->getType();
- auto alloc_size = static_cast(dl.getTypeAllocSize(arg_type));
- llvm::Function *func = nullptr;
-
- if (arg_type->isX86_FP80Ty()) {
- func = gModule->getFunction("__remill_write_memory_f80");
- arg_val = ir.CreateFPTrunc(arg_val, llvm::Type::getDoubleTy(*gContext));
-
- } else if (arg_type->isDoubleTy()) {
- func = gModule->getFunction("__remill_write_memory_f64");
-
- } else if (arg_type->isFloatTy()) {
- func = gModule->getFunction("__remill_write_memory_f32");
-
- } else if (arg_type->isIntegerTy()) {
- func = WriteIntToMemFunc(alloc_size);
-
- if (dl.getTypeSizeInBits(arg_type) <
- dl.getTypeAllocSizeInBits(arg_type)) {
- arg_type = llvm::Type::getIntNTy(
- *gContext, static_cast(alloc_size * 8));
- arg_val = ir.CreateZExt(arg_val, arg_type);
- }
-
- } else if (arg_type->isPointerTy()) {
- if (32 == gArch->address_size) {
- func = gModule->getFunction("__remill_write_memory_32");
- } else {
- func = gModule->getFunction("__remill_write_memory_64");
- }
- arg_val = ir.CreatePtrToInt(arg_val, gWordType);
- }
-
- CHECK(func != nullptr)
- << "Could not find remill memory write intrinsic to write a "
- << alloc_size << "-byte value of type "
- << remill::LLVMThingToString(arg_type) << " to the stack.";
-
- // Store the argument to the stack memory.
- args[0] = memory;
- args[1] = sp;
- args[2] = arg_val;
- memory = ir.CreateCall(func, args);
-
- // Bump the stack pointer.
- alloc_size = std::max(alloc_size, addr_size);
- sp = ir.CreateSub(sp, llvm::ConstantInt::get(gWordType, alloc_size));
- num_stored_stack_bytes += alloc_size;
- }
-
- ir.CreateStore(memory, memory_ref); // Update the memory pointer.
-
- StoreStackPointer(block, sp);
-}
-
-void CallingConvention::FreeArguments(llvm::BasicBlock *block) {
- if (!num_stored_stack_bytes) {
- return;
- }
-
- if (llvm::CallingConv::X86_StdCall == cc ||
- llvm::CallingConv::X86_ThisCall == cc) {
- return; // Callee cleanup.
- }
-
- auto sp = LoadStackPointer(block);
-
- llvm::IRBuilder<> ir(block);
- sp = ir.CreateAdd(
- sp, llvm::ConstantInt::get(gWordType, num_stored_stack_bytes));
-
- StoreStackPointer(block, sp);
-}
-
-void CallingConvention::AllocateReturnAddress(llvm::BasicBlock *block) {
- if (gArch->IsAArch64()) {
- return; // Return address is passed through the link pointer.
-
- // The stack grows down on x86/amd64.
- } else if (gArch->IsX86() || gArch->IsAMD64()) {
- llvm::IRBuilder<> ir(block);
-
- auto addr_size = gArch->address_size / 8;
-
- if (llvm::CallingConv::Win64 == cc) {
- CHECK(gArch->IsAMD64());
- addr_size += 32; // Shadow space.
- }
-
- auto addr_size_bytes = llvm::ConstantInt::get(gWordType, addr_size);
- StoreStackPointer(
- block, ir.CreateSub(LoadStackPointer(block), addr_size_bytes));
-
- } else {
- LOG(FATAL)
- << "Cannot allocate space for return address for architecture "
- << remill::GetArchName(gArch->arch_name) << " and calling convention "
- << cc;
- }
-}
-
-void CallingConvention::FreeReturnAddress(llvm::BasicBlock *block) {
- if (gArch->IsAArch64()) {
- auto x30 = remill::FindVarInFunction(block, "X30");
- llvm::IRBuilder<> ir(block);
- auto ret_addr = ir.CreateLoad(x30);
- remill::StoreProgramCounter(block, ret_addr);
-
- // The stack grows down on x86/amd64.
- } else if (gArch->IsX86() || gArch->IsAMD64()) {
- llvm::IRBuilder<> ir(block);
- auto addr_size = gArch->address_size / 8;
- auto addr_size_bytes = llvm::ConstantInt::get(gWordType, addr_size);
- auto sp = LoadStackPointer(block);
- auto read_ret_addr = ReadIntFromMemFunc(addr_size);
- llvm::Value *read_ret_addr_args[] = {remill::LoadMemoryPointer(block), sp};
- auto ret_addr = ir.CreateCall(read_ret_addr, read_ret_addr_args);
- remill::StoreProgramCounter(block, ret_addr);
-
- StoreStackPointer(
- block, ir.CreateAdd(sp, addr_size_bytes));
-
- } else {
- LOG(FATAL)
- << "Cannot allocate space for return address for architecture "
- << remill::GetArchName(gArch->arch_name) << " and calling convention "
- << cc;
- }
-}
-
-llvm::Value *CallingConvention::LoadReturnValue(llvm::BasicBlock *block,
- llvm::Type *val_type) {
- llvm::IRBuilder<> ir(block);
-
- if (!val_type) {
- val_type = gWordType;
- }
-
- //auto val_var = ReturnValVar(cc, val_type);
- auto val_var = GetVarForNextReturn(val_type);
- return ir.CreateLoad(ir.CreateBitCast(
- remill::FindVarInFunction(block, val_var),
- llvm::PointerType::get(val_type, 0)));
-}
-
-llvm::Value *CallingConvention::LoadStackPointer(llvm::BasicBlock *block) {
- llvm::IRBuilder<> ir(block);
- return ir.CreateLoad(remill::FindVarInFunction(block, sp_name, false));
-}
-
-void CallingConvention::StoreStackPointer(llvm::BasicBlock *block,
- llvm::Value *new_val) {
- llvm::IRBuilder<> ir(block);
- auto val_type = new_val->getType();
- if (val_type->isPointerTy()) {
- new_val = ir.CreatePtrToInt(new_val, gWordType);
- }
- ir.CreateStore(
- new_val,
- remill::FindVarInFunction(block, StackPointerVarName()));
-}
-
-void CallingConvention::StoreThreadPointer(llvm::BasicBlock *block,
- llvm::Value *new_val) {
- llvm::IRBuilder<> ir(block);
- auto val_type = new_val->getType();
- if (val_type->isPointerTy()) {
- new_val = ir.CreatePtrToInt(new_val, gWordType);
- }
- ir.CreateStore(
- new_val,
- remill::FindVarInFunction(block, ThreadPointerVarName()));
-}
-
-// Return the address of the base of the TLS data.
-llvm::Value *GetTLSBaseAddress(llvm::IRBuilder<> &ir) {
- enum {
- kGSAddressSpace = 256U,
- kFSAddressSpace = 257U,
-
- // From inside of the TEB.
- kWin32TLSPointerIndex = 0x2c,
- kWin64TLSPointerIndex = 0x58
- };
-
- if (gArch->IsAArch64()) {
-
-#if LLVM_VERSION(3, 7) >= LLVM_VERSION_NUMBER
- LOG(ERROR)
- << "LLVM 3.7 and below have no AArch64 thread pointer-related "
- << "intrinsics; using NULL as the base of TLS.";
- return llvm::ConstantInt::get(gWordType, 0);
-
-#elif LLVM_VERSION(3, 8) >= LLVM_VERSION_NUMBER
- LOG(ERROR)
- << "Assuming the `llvm.arm.thread.pointer` intrinsic gets us the base "
- << "of thread-local storage.";
- auto func = llvm::Intrinsic::getDeclaration(
- gModule.get(), llvm::Intrinsic::arm_thread_pointer);
- return ir.CreatePtrToInt(ir.CreateCall(func), gWordType);
-#else
- LOG(ERROR)
- << "Assuming the `thread.pointer` intrinsic gets us the base "
- << "of thread-local storage.";
- auto func = llvm::Intrinsic::getDeclaration(
- gModule.get(), llvm::Intrinsic::thread_pointer);
- return ir.CreatePtrToInt(ir.CreateCall(func), gWordType);
-#endif
-
- // 64-bit x86.
- } else if (gArch->IsAMD64()) {
- llvm::ConstantInt *base = nullptr;
- unsigned addr_space = 0;
- if (remill::kOSWindows == gArch->os_name) {
- base = llvm::ConstantInt::get(gWordType, kWin64TLSPointerIndex);
- addr_space = kGSAddressSpace;
- } else if (remill::kOSLinux == gArch->os_name) {
- base = llvm::ConstantInt::get(gWordType, 0);
- addr_space = kFSAddressSpace;
- }
-
- if (base) {
- auto tls_base_ptr = ir.CreateIntToPtr(
- base, llvm::PointerType::get(gWordType, addr_space));
- return ir.CreateLoad(tls_base_ptr);
- }
-
- // 32-bit x86.
- } else if (gArch->IsX86()) {
- llvm::ConstantInt *base = nullptr;
- unsigned addr_space = 0;
- if (remill::kOSWindows == gArch->os_name) {
- base = llvm::ConstantInt::get(gWordType, kWin32TLSPointerIndex);
- addr_space = kFSAddressSpace;
- } else if (remill::kOSLinux == gArch->os_name) {
- base = llvm::ConstantInt::get(gWordType, 0);
- addr_space = kGSAddressSpace;
- }
-
- if (base) {
- auto tls_base_ptr = ir.CreateIntToPtr(
- base, llvm::PointerType::get(gWordType, addr_space));
- return ir.CreateLoad(tls_base_ptr);
- }
- }
-
- LOG(FATAL)
- << "Cannot generate code to find the thread base pointer for arch "
- << remill::GetArchName(gArch->arch_name) << " and OS "
- << remill::GetOSName(gArch->os_name);
- return nullptr;
-}
-
-} // namespace mcsema
diff --git a/mcsema/Arch/ABI.h b/mcsema/Arch/ABI.h
deleted file mode 100644
index 7552c2e6b..000000000
--- a/mcsema/Arch/ABI.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (c) 2017 Trail of Bits, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef MCSEMA_ARCH_ABI_H_
-#define MCSEMA_ARCH_ABI_H_
-
-#include
-#include
-
-#include "remill/BC/Compat/CallingConvention.h"
-
-namespace llvm {
-class Type;
-} // namespace llvm
-namespace mcsema {
-
-struct ArgConstraint {
- std::string var_name;
- const int accepted_val_kinds;
-};
-
-// Generic functions for reading/writing to the register/stack state in a way
-// that respects a supplied calling convention.
-class CallingConvention {
- public:
- explicit CallingConvention(llvm::CallingConv::ID cc_);
-
- llvm::Value *LoadNextArgument(llvm::BasicBlock *block,
- llvm::Type *goal_type=nullptr,
- bool is_byval=false);
-
- void StoreReturnValue(llvm::BasicBlock *block, llvm::Value *ret_val);
-
- void StoreArguments(llvm::BasicBlock *block,
- const std::vector &arg_vals);
-
- void FreeArguments(llvm::BasicBlock *block);
-
- void AllocateReturnAddress(llvm::BasicBlock *block);
- void FreeReturnAddress(llvm::BasicBlock *block);
-
- llvm::Value *LoadReturnValue(llvm::BasicBlock *block,
- llvm::Type *goal_type=nullptr);
-
- llvm::Value *LoadStackPointer(llvm::BasicBlock *block);
-
- void StoreStackPointer(llvm::BasicBlock *block, llvm::Value *new_val);
-
- const char *StackPointerVarName(void) const {
- return sp_name;
- }
-
- void StoreThreadPointer(llvm::BasicBlock *block, llvm::Value *new_val);
-
- const char *ThreadPointerVarName(void) const {
- return tp_name;
- }
-
- private:
- void StoreVectorRetValue(llvm::BasicBlock *block,
- llvm::Value *ret_val,
- llvm::VectorType *goal_type);
-
- llvm::Value *LoadVectorArgument(llvm::BasicBlock *block,
- llvm::VectorType *goal_type);
- llvm::Value *LoadNextSimpleArgument(llvm::BasicBlock *block,
- llvm::Type *goal_type);
-
- const char *GetVarForNextArgument(llvm::Type *val_type);
- const char *GetVarForNextReturn(llvm::Type *val_type);
-
- llvm::CallingConv::ID cc;
- uint64_t used_reg_bitmap;
- uint64_t used_return_bitmap;
- uint64_t num_loaded_stack_bytes;
- uint64_t num_stored_stack_bytes;
- const char * const sp_name;
- const char * const tp_name;
- const std::vector ®_table;
- const std::vector &return_table;
- CallingConvention(void) = delete;
-};
-
-// Return the address of the base of the TLS data.
-llvm::Value *GetTLSBaseAddress(llvm::IRBuilder<> &ir);
-
-} // namespace mcsema
-
-#endif // MCSEMA_ARCH_ABI_H_
diff --git a/mcsema/Arch/Arch.cpp b/mcsema/Arch/Arch.cpp
index e20a03ad1..4b47806a4 100644
--- a/mcsema/Arch/Arch.cpp
+++ b/mcsema/Arch/Arch.cpp
@@ -1,19 +1,22 @@
/*
- * Copyright (c) 2017 Trail of Bits, Inc.
+ * Copyright (c) 2020 Trail of Bits, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
+#include "mcsema/Arch/Arch.h"
+
#include
#include
@@ -21,6 +24,7 @@
#include
#include
+#include
#include
#include
#include
@@ -31,24 +35,31 @@
#include
#include
-#include "remill/Arch/Arch.h"
+#include
-#include "mcsema/Arch/Arch.h"
#include "mcsema/BC/Util.h"
namespace mcsema {
extern std::shared_ptr gContext;
-const remill::Arch *gArch = nullptr;
+std::unique_ptr gArch(nullptr);
bool InitArch(const std::string &os, const std::string &arch) {
LOG(INFO)
<< "Initializing for " << arch << " code on " << os;
- gArch = remill::GetTargetArch(*gContext);
+ remill::Arch::GetTargetArch(*gContext).swap(gArch);
gWordType = llvm::Type::getIntNTy(
*gContext, static_cast(gArch->address_size));
+
+ gWordMask = 0;
+ if (32 == gArch->address_size) {
+ gWordMask = static_cast(~0u);
+ } else {
+ gWordMask = ~gWordMask;
+ }
+
return true;
}
diff --git a/mcsema/Arch/Arch.h b/mcsema/Arch/Arch.h
index 0dbcaf604..9e54e5808 100644
--- a/mcsema/Arch/Arch.h
+++ b/mcsema/Arch/Arch.h
@@ -1,22 +1,23 @@
/*
- * Copyright (c) 2017 Trail of Bits, Inc.
+ * Copyright (c) 2020 Trail of Bits, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
-#ifndef MCSEMA_ARCH_ARCH_H_
-#define MCSEMA_ARCH_ARCH_H_
+#pragma once
+#include
#include
namespace remill {
@@ -25,10 +26,8 @@ class Arch;
namespace mcsema {
-extern const remill::Arch *gArch;
+extern std::unique_ptr gArch;
bool InitArch(const std::string &os, const std::string &arch);
} // namespace mcsema
-
-#endif // MCSEMA_ARCH_ARCH_H_
diff --git a/mcsema/Arch/X86/Runtime/CMakeLists.txt b/mcsema/Arch/X86/Runtime/CMakeLists.txt
index 6b5878d89..6af8adee5 100644
--- a/mcsema/Arch/X86/Runtime/CMakeLists.txt
+++ b/mcsema/Arch/X86/Runtime/CMakeLists.txt
@@ -1,16 +1,17 @@
-# Copyright (c) 2017 Trail of Bits, Inc.
+# Copyright (c) 2020 Trail of Bits, Inc.
#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
#
-# http://www.apache.org/licenses/LICENSE-2.0
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
enable_language(ASM)
@@ -206,10 +207,19 @@ else()
endif()
endif()
+
+if(DEFINED REMILL_INCLUDE_LOCATION)
+ set(REMILL_INCLUDE "-I${REMILL_INCLUDE_LOCATION}")
+endif()
+
+if(DEFINED REMILL_SOURCE_DIR)
+ set(REMILL_SOURCE "-I${REMILL_SOURCE_DIR}")
+endif()
+
# Create a runtime.
add_custom_command(
OUTPUT runtime_32.o
- COMMAND "${CMAKE_CXX_COMPILER}" -std=gnu++11 -isystem "${REMILL_INCLUDE_LOCATION}" -isystem "${MCSEMA_SOURCE_DIR}" -m32 -fPIC -c "${CMAKE_CURRENT_SOURCE_DIR}/Runtime.cpp" -o runtime_32.o
+ COMMAND "${CMAKE_CXX_COMPILER}" -std=gnu++11 "${REMILL_INCLUDE}" "${REMILL_SOURCE}" "${REMILL_SOURCE_DIR}" -I"${MCSEMA_SOURCE_DIR}" -m32 -fPIC -c "${CMAKE_CURRENT_SOURCE_DIR}/Runtime.cpp" -o runtime_32.o
DEPENDS Runtime.cpp
COMMENT "Building 32-bit runtime"
)
@@ -225,7 +235,7 @@ set_source_files_properties(
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
add_custom_command(
OUTPUT runtime_64.o
- COMMAND "${CMAKE_CXX_COMPILER}" -std=gnu++11 -isystem "${REMILL_INCLUDE_LOCATION}" -isystem "${MCSEMA_SOURCE_DIR}" -m64 -fPIC -c "${CMAKE_CURRENT_SOURCE_DIR}/Runtime.cpp" -o runtime_64.o
+ COMMAND "${CMAKE_CXX_COMPILER}" -std=gnu++11 "${REMILL_INCLUDE}" "${REMILL_SOURCE}" -I"${MCSEMA_SOURCE_DIR}" -m64 -fPIC -c "${CMAKE_CURRENT_SOURCE_DIR}/Runtime.cpp" -o runtime_64.o
DEPENDS Runtime.cpp
COMMENT "Building 64-bit runtime"
)
@@ -237,3 +247,21 @@ if(CMAKE_SIZEOF_VOID_P EQUAL 8)
GENERATED True
)
endif()
+
+add_runtime(runtime_x86
+ SOURCES "Runtime.cpp"
+ ADDRESS_SIZE 32
+ BCFLAGS "-xc++" "-m32" "-std=gnu++17" "-Wno-deprecated-declarations"
+ INCLUDEDIRECTORIES "${CMAKE_SOURCE_DIR}" "${MCSEMA_SOURCE_DIR}" "${REMILL_INCLUDE_LOCATION}"
+ INSTALLDESTINATION "${install_folder}/lib"
+)
+
+if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ add_runtime(runtime_amd64
+ SOURCES "Runtime.cpp"
+ ADDRESS_SIZE 64
+ BCFLAGS "-xc++" "-m64" "-std=gnu++17" "-Wno-deprecated-declarations"
+ INCLUDEDIRECTORIES "${CMAKE_SOURCE_DIR}" "${MCSEMA_SOURCE_DIR}" "${REMILL_INCLUDE_LOCATION}"
+ INSTALLDESTINATION "${install_folder}/lib"
+ )
+endif()
diff --git a/mcsema/Arch/X86/Runtime/Registers.h b/mcsema/Arch/X86/Runtime/Registers.h
index e75c62eff..16e4cb8a6 100644
--- a/mcsema/Arch/X86/Runtime/Registers.h
+++ b/mcsema/Arch/X86/Runtime/Registers.h
@@ -1,17 +1,18 @@
/*
- * Copyright (c) 2017 Trail of Bits, Inc.
+ * Copyright (c) 2020 Trail of Bits, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
#ifndef MCSEMA_ARCH_X86_RUNTIME_REGISTERS_H_
@@ -77,23 +78,23 @@
#define R14D gpr.r14.dword
#define R15D gpr.r15.dword
-#define RAX gpr.rax.qword
-#define RBX gpr.rbx.qword
-#define RCX gpr.rcx.qword
-#define RDX gpr.rdx.qword
-#define RSI gpr.rsi.qword
-#define RDI gpr.rdi.qword
-#define RSP gpr.rsp.qword
-#define RBP gpr.rbp.qword
-#define R8 gpr.r8.qword
-#define R9 gpr.r9.qword
-#define R10 gpr.r10.qword
-#define R11 gpr.r11.qword
-#define R12 gpr.r12.qword
-#define R13 gpr.r13.qword
-#define R14 gpr.r14.qword
-#define R15 gpr.r15.qword
-#define RIP gpr.rip.qword
+#define RAX gpr.rax.aword
+#define RBX gpr.rbx.aword
+#define RCX gpr.rcx.aword
+#define RDX gpr.rdx.aword
+#define RSI gpr.rsi.aword
+#define RDI gpr.rdi.aword
+#define RSP gpr.rsp.aword
+#define RBP gpr.rbp.aword
+#define R8 gpr.r8.aword
+#define R9 gpr.r9.aword
+#define R10 gpr.r10.aword
+#define R11 gpr.r11.aword
+#define R12 gpr.r12.aword
+#define R13 gpr.r13.aword
+#define R14 gpr.r14.aword
+#define R15 gpr.r15.aword
+#define RIP gpr.rip.aword
#define SS seg.ss
#define ES seg.es
diff --git a/mcsema/Arch/X86/Runtime/Runtime.cpp b/mcsema/Arch/X86/Runtime/Runtime.cpp
index c03b70d4d..2dd058160 100644
--- a/mcsema/Arch/X86/Runtime/Runtime.cpp
+++ b/mcsema/Arch/X86/Runtime/Runtime.cpp
@@ -1,27 +1,32 @@
/*
- * Copyright (c) 2017 Trail of Bits, Inc.
+ * Copyright (c) 2020 Trail of Bits, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
#include
#include
#include
#include
+#include
#define HAS_FEATURE_AVX 1
#define HAS_FEATURE_AVX512 0
-#define ADDRESS_SIZE_BITS 64
+
+#ifndef ADDRESS_SIZE_BITS
+# define ADDRESS_SIZE_BITS 64
+#endif
#include "remill/Arch/X86/Runtime/State.h"
#include "mcsema/Arch/X86/Runtime/Registers.h"
@@ -34,6 +39,12 @@
# define PRIx32 "x"
#endif
+#if ADDRESS_SIZE_BITS == 64
+# define PRIxADDR PRIx64
+#else
+# define PRIxADDR PRIx32
+#endif
+
extern "C" {
// Debug registers.
@@ -130,16 +141,15 @@ Memory *__remill_sync_hyper_call(
}
Memory *__mcsema_reg_tracer(State &state, addr_t, Memory *memory) {
- const char *format = nullptr;
if (sizeof(void *) == 8) {
fprintf(
stderr,
- "RIP=%" PRIx64 ",RAX=%" PRIx64 ",RBX=%" PRIx64
- ",RCX=%" PRIx64 ",RDX=%" PRIx64 ",RSI=%" PRIx64
- ",RDI=%" PRIx64 ",RBP=%" PRIx64 ",RSP=%" PRIx64
- ",R8=%" PRIx64 ",R9=%" PRIx64 ",R10=%" PRIx64
- ",R11=%" PRIx64 ",R12=%" PRIx64 ",R13=%" PRIx64
- ",R14=%" PRIx64 ",R15=%" PRIx64 "\n",
+ "RIP=%" PRIxADDR ",RAX=%" PRIxADDR ",RBX=%" PRIxADDR
+ ",RCX=%" PRIxADDR ",RDX=%" PRIxADDR ",RSI=%" PRIxADDR
+ ",RDI=%" PRIxADDR ",RBP=%" PRIxADDR ",RSP=%" PRIxADDR
+ ",R8=%" PRIxADDR ",R9=%" PRIxADDR ",R10=%" PRIxADDR
+ ",R11=%" PRIxADDR ",R12=%" PRIxADDR ",R13=%" PRIxADDR
+ ",R14=%" PRIxADDR ",R15=%" PRIxADDR "\n",
state.RIP, state.RAX, state.RBX, state.RCX, state.RDX, state.RSI,
state.RDI, state.RBP, state.RSP, state.R8, state.R9, state.R10,
diff --git a/mcsema/Arch/X86/Runtime/print_ELF_32_linux.cpp b/mcsema/Arch/X86/Runtime/print_ELF_32_linux.cpp
index 753f84d09..9643425d3 100644
--- a/mcsema/Arch/X86/Runtime/print_ELF_32_linux.cpp
+++ b/mcsema/Arch/X86/Runtime/print_ELF_32_linux.cpp
@@ -1,17 +1,18 @@
/*
- * Copyright (c) 2017 Trail of Bits, Inc.
+ * Copyright (c) 2020 Trail of Bits, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
#include
diff --git a/mcsema/Arch/X86/Runtime/print_ELF_64_linux.cpp b/mcsema/Arch/X86/Runtime/print_ELF_64_linux.cpp
index 1e08484c1..ac8af3228 100644
--- a/mcsema/Arch/X86/Runtime/print_ELF_64_linux.cpp
+++ b/mcsema/Arch/X86/Runtime/print_ELF_64_linux.cpp
@@ -1,17 +1,18 @@
/*
- * Copyright (c) 2017 Trail of Bits, Inc.
+ * Copyright (c) 2020 Trail of Bits, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
#include
diff --git a/mcsema/Arch/X86/Runtime/print_PE_32_windows.cpp b/mcsema/Arch/X86/Runtime/print_PE_32_windows.cpp
index 7d0c73734..c40fc262e 100644
--- a/mcsema/Arch/X86/Runtime/print_PE_32_windows.cpp
+++ b/mcsema/Arch/X86/Runtime/print_PE_32_windows.cpp
@@ -1,17 +1,18 @@
/*
- * Copyright (c) 2017 Trail of Bits, Inc.
+ * Copyright (c) 2020 Trail of Bits, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
#include
diff --git a/mcsema/Arch/X86/Runtime/print_PE_64_windows.cpp b/mcsema/Arch/X86/Runtime/print_PE_64_windows.cpp
index a9fdd6443..c48835cbd 100644
--- a/mcsema/Arch/X86/Runtime/print_PE_64_windows.cpp
+++ b/mcsema/Arch/X86/Runtime/print_PE_64_windows.cpp
@@ -1,17 +1,18 @@
/*
- * Copyright (c) 2017 Trail of Bits, Inc.
+ * Copyright (c) 2020 Trail of Bits, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
#include
diff --git a/mcsema/BC/Callback.cpp b/mcsema/BC/Callback.cpp
index 12c3493ef..6f5f5145c 100644
--- a/mcsema/BC/Callback.cpp
+++ b/mcsema/BC/Callback.cpp
@@ -1,19 +1,22 @@
/*
- * Copyright (c) 2017 Trail of Bits, Inc.
+ * Copyright (c) 2020 Trail of Bits, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
+#include "mcsema/BC/Callback.h"
+
#include
#include
@@ -30,16 +33,19 @@
#include
#include
-#include "remill/Arch/Arch.h"
-#include "remill/Arch/Name.h"
-#include "remill/BC/ABI.h"
-#include "remill/BC/Annotate.h"
-#include "remill/BC/Util.h"
-#include "remill/BC/Version.h"
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
-#include "mcsema/Arch/ABI.h"
#include "mcsema/Arch/Arch.h"
-#include "mcsema/BC/Callback.h"
#include "mcsema/BC/Legacy.h"
#include "mcsema/BC/Segment.h"
#include "mcsema/BC/Util.h"
@@ -54,15 +60,19 @@ DEFINE_bool(explicit_args, false,
"bitcode, especially where floating point argument and return "
"values are concerned.");
-DEFINE_uint64(explicit_args_count, 8,
+DEFINE_uint32(explicit_args_count, 8,
"Number of explicit (integer) arguments to pass to an unknown "
"function, or to accept from an unknown function. This value is "
- "used when ");
+ "used when calling external functions for which no type "
+ "information is known, or who take a variable number of arguments.");
-DEFINE_uint64(explicit_args_stack_size, 4096 * 256 /* 1 MiB */,
+DEFINE_uint32(explicit_args_stack_size, 4096 * 256 /* 1 MiB */,
"Size of the stack of the emulated program when the program "
"is lifted using --explicit_args.");
+DEFINE_uint32(explicit_args_tls_size, 4 * 4096,
+ "Number of bytes of thread local storage");
+
DECLARE_bool(stack_protector);
namespace mcsema {
@@ -153,15 +163,17 @@ static llvm::Function *ImplementNativeToLiftedCallback(
// Create the callback function that calls the inline assembly.
auto callback_type = llvm::FunctionType::get(void_type, false);
- auto callback_func = llvm::Function::Create(
- callback_type, llvm::GlobalValue::InternalLinkage, // Tentative linkage.
- callback_name, gModule.get());
+ auto callback_func = gModule->getFunction(callback_name);
+ if (!callback_func) {
+ callback_func = llvm::Function::Create(
+ callback_type, llvm::GlobalValue::InternalLinkage, // Tentative linkage.
+ callback_name, gModule.get());
+ }
callback_func->setVisibility(llvm::GlobalValue::DefaultVisibility);
callback_func->addFnAttr(llvm::Attribute::Naked);
callback_func->addFnAttr(llvm::Attribute::NoInline);
callback_func->addFnAttr(llvm::Attribute::NoBuiltin);
-
// Create the inline assembly. We use memory operands (
std::vector asm_arg_types;
std::vector asm_args;
@@ -222,7 +234,12 @@ static llvm::Function *ImplementNativeToLiftedCallback(
asm_args.push_back(attach_func_ptr);
ir.CreateCall(asm_func, asm_args);
- ir.CreateRetVoid();
+
+ if (auto ret_type = callback_func->getReturnType(); ret_type->isVoidTy()) {
+ ir.CreateRetVoid();
+ } else {
+ ir.CreateRet(llvm::UndefValue::get(ret_type));
+ }
if (!FLAGS_pc_annotation.empty()) {
legacy::AnnotateInsts(callback_func, cfg_func->ea);
@@ -238,20 +255,30 @@ static llvm::Function *ImplementNativeToLiftedCallback(
// Create a stack and a variable that tracks the stack pointer.
static llvm::Constant *InitialStackPointerValue(void) {
- auto stack_type = llvm::ArrayType::get(
- gWordType, FLAGS_explicit_args_stack_size / (gArch->address_size / 8));
+ unsigned min_frame_size = 512u;
+ const auto num_bytes = std::max(FLAGS_explicit_args_stack_size,
+ 4096u + min_frame_size);
+ auto i8_type = llvm::Type::getInt8Ty(*gContext);
+ auto stack_type = llvm::ArrayType::get(i8_type, num_bytes);
- static llvm::GlobalVariable *stack = nullptr;
+ static llvm::Constant *stack = nullptr;
if (!stack) {
- stack = new llvm::GlobalVariable(
+ auto stack_var = new llvm::GlobalVariable(
*gModule, stack_type, false, llvm::GlobalValue::InternalLinkage,
- llvm::Constant::getNullValue(stack_type), "__mcsema_stack");
- stack->setThreadLocal(true);
+ llvm::ConstantAggregateZero::get(stack_type), "__mcsema_stack",
+ nullptr, llvm::GlobalValue::InitialExecTLSModel);
+ stack = stack_var;
+
+ if (stack_var->getType()->getAddressSpace()) {
+ stack = llvm::ConstantExpr::getAddrSpaceCast(
+ stack_var, llvm::PointerType::get(stack_type, 0));
+ }
}
- std::vector indexes(2);
- indexes[0] = llvm::ConstantInt::get(gWordType, 0);
- indexes[1] = llvm::ConstantInt::get(
- gWordType, stack_type->getNumElements() - 8);
+
+ const auto i32_ty = llvm::Type::getInt32Ty(*gContext);
+ llvm::Constant *indexes[2];
+ indexes[0] = llvm::ConstantInt::get(i32_ty, 0);
+ indexes[1] = llvm::ConstantInt::get(i32_ty, num_bytes - min_frame_size);
#if LLVM_VERSION_NUMBER <= LLVM_VERSION(3, 6)
auto gep = llvm::ConstantExpr::getInBoundsGetElementPtr(stack, indexes);
@@ -262,88 +289,106 @@ static llvm::Constant *InitialStackPointerValue(void) {
return llvm::ConstantExpr::getPtrToInt(gep, gWordType);
}
-// Create an array of data for holding thread-local storage.
-static llvm::Constant *InitialThreadLocalStorage(void) {
- static llvm::Constant *tls = nullptr;
- if (tls) {
- return tls;
+static const char *ThreadPointerNameX86(void) {
+ switch (gArch->os_name) {
+ case remill::kOSLinux:
+ return "GS_BASE";
+ case remill::kOSWindows:
+ return "FS_BASE";
+ default:
+ return nullptr;
+ }
+}
+
+static const char *ThreadPointerNameAMD64(void) {
+ switch (gArch->os_name) {
+ case remill::kOSLinux:
+ return "FS_BASE";
+ case remill::kOSWindows:
+ return "GS_BASE";
+ default:
+ return nullptr;
+ }
+}
+
+static const char *ThreadPointerName(void) {
+ const char *tp_name = nullptr;
+ switch (gArch->arch_name) {
+ case remill::kArchAArch64LittleEndian:
+ return "TPIDR_EL0";
+
+ case remill::kArchX86:
+ case remill::kArchX86_AVX:
+ case remill::kArchX86_AVX512:
+ tp_name = ThreadPointerNameX86();
+ break;
+
+ case remill::kArchAMD64:
+ case remill::kArchAMD64_AVX:
+ case remill::kArchAMD64_AVX512:
+ tp_name = ThreadPointerNameAMD64();
+ break;
+
+ default:
+ break;
}
- auto tls_type = llvm::ArrayType::get(
- gWordType, 4096 / (gArch->address_size / 8));
+ LOG_IF(ERROR, !tp_name)
+ << "Can't get thread pointer name for architecture "
+ << remill::GetArchName(gArch->arch_name) << " and OS "
+ << remill::GetOSName(gArch->os_name);
+ return tp_name;
+}
+
+// Create an array of data for holding thread-local storage.
+static llvm::Constant *InitialThreadLocalStorage(void) {
+
+ // Add some TLS pages.
+ llvm::ArrayType *tls_type = llvm::ArrayType::get(
+ gWordType, FLAGS_explicit_args_tls_size / (gArch->address_size / 8));
auto tls_var = new llvm::GlobalVariable(
*gModule, tls_type, false, llvm::GlobalValue::InternalLinkage,
- llvm::Constant::getNullValue(tls_type), "__mcsema_tls");
- tls_var->setThreadLocal(true);
+ llvm::Constant::getNullValue(tls_type), "__mcsema_tls",
+ nullptr, llvm::GlobalValue::InitialExecTLSModel);
- std::vector indexes(2);
- indexes[0] = llvm::ConstantInt::get(gWordType, 0);
+ llvm::Constant *tls = tls_var;
+ if (tls_var->getType()->getAddressSpace()) {
+ tls = llvm::ConstantExpr::getAddrSpaceCast(
+ tls_var, llvm::PointerType::get(tls_type, 0));
+ }
+
+ const auto i32_ty = llvm::Type::getInt32Ty(*gContext);
+ llvm::Constant *indexes[2];
+ indexes[0] = llvm::ConstantInt::get(i32_ty, 0);
indexes[1] = indexes[0];
#if LLVM_VERSION_NUMBER <= LLVM_VERSION(3, 6)
- auto gep = llvm::ConstantExpr::getInBoundsGetElementPtr(tls_var, indexes);
+ tls = llvm::ConstantExpr::getInBoundsGetElementPtr(tls, indexes);
#else
- auto gep = llvm::ConstantExpr::getInBoundsGetElementPtr(
- nullptr, tls_var, indexes);
+ tls = llvm::ConstantExpr::getInBoundsGetElementPtr(
+ nullptr, tls, indexes);
#endif
- tls = llvm::ConstantExpr::getPtrToInt(gep, gWordType);
+ tls = llvm::ConstantExpr::getPtrToInt(tls, gWordType);
return tls;
}
-// Figure ouf the byte offset of the stack pointer register in the `State`
-// structure.
-static uint64_t GetStackPointerOffset(void) {
- CallingConvention cc(gArch->DefaultCallingConv());
- std::string sp_name(cc.StackPointerVarName());
- auto reg = gArch->RegisterByName(sp_name);
- CHECK(reg != nullptr)
- << "Could not identify stack pointer " << sp_name
- << " register in State structure";
-
- return reg->offset;
-}
-
-// Create a global register state pointer to pass to lifted functions.
-static llvm::GlobalVariable *GetStatePointer(void) {
- static llvm::GlobalVariable *state_ptr = nullptr;
- if (state_ptr) {
- return state_ptr;
- }
-
- auto lifted_func_type = LiftedFunctionType();
- auto state_ptr_type = lifted_func_type->getFunctionParamType(
- remill::kStatePointerArgNum);
- auto state_type = llvm::dyn_cast(
- state_ptr_type)->getElementType();
-
- // State is initialized with zeroes. Each callback/entrypoint set
- // appropriate value to stack pointer. This is needed because of
- // thread_local
- auto state_init = llvm::ConstantAggregateZero::get(state_type);
- state_ptr = new llvm::GlobalVariable(
- *gModule, state_type, false, llvm::GlobalValue::InternalLinkage,
- state_init, "__mcsema_reg_state");
- state_ptr->setThreadLocal(true);
- return state_ptr;
-}
-
-// note(lukas): We don't need to annotate, it will always be inlined
+// NOTE(lukas): We don't need to annotate, it will always be inlined.
static llvm::Function *CreateVerifyRegState(void) {
- auto *func_type = llvm::FunctionType::get(llvm::Type::getVoidTy(*gContext),
- {}, false);
+ auto reg_state = GetStatePointer();
+ auto *func_type = llvm::FunctionType::get(reg_state->getType(), false);
auto new_func = gModule->getOrInsertFunction(
- "__mcsema_verify_reg_state",
+ "__mcsema_init_reg_state",
func_type);
auto func = llvm::dyn_cast(
new_func IF_LLVM_GTE_900(.getCallee()));
CHECK(func != nullptr)
- << "Could not get or create function '__mcsema_verify_reg_state'";
+ << "Could not get or create function '__mcsema_init_reg_state'";
- auto sp_offset = GetStackPointerOffset();
- auto reg_state = GetStatePointer();
+ const auto sp_name = gArch->StackPointerRegisterName();
+ auto sp_reg = gArch->RegisterByName(sp_name);
auto entry_block = llvm::BasicBlock::Create(*gContext, "entry", func);
auto is_null_block = llvm::BasicBlock::Create(*gContext, "is_null", func);
@@ -352,38 +397,63 @@ static llvm::Function *CreateVerifyRegState(void) {
// Need to find out where stack pointer is and known information is
// byte offset in state structure
- auto byte_ty = llvm::Type::getInt8PtrTy(*gContext);
+// auto byte_ty = llvm::Type::getInt8PtrTy(*gContext);
unsigned ptr_size = static_cast(gArch->address_size);
auto reg_ptr_ty = llvm::PointerType::getIntNPtrTy(*gContext, ptr_size);
-
- auto casted_reg_state = ir.CreateBitCast(reg_state, byte_ty);
- auto rsp = ir.CreateGEP(casted_reg_state, ir.getInt64(sp_offset));
+ //TODO(lukas): remove after abi_libraries patch gets merged into master
+ auto GetConstantInt = [&](unsigned size, uint64_t value) {
+ return llvm::ConstantInt::get(
+ llvm::Type::getIntNTy(*gContext, size), value);
+ };
+// auto casted_reg_state = ir.CreateBitCast(reg_state, byte_ty);
+ auto rsp = sp_reg->AddressOf(reg_state, entry_block);
auto casted_rsp = ir.CreateBitCast(rsp, reg_ptr_ty);
- auto rsp_val = ir.CreateLoad(casted_rsp,
- llvm::Type::getIntNTy(*gContext, ptr_size));
+ auto rsp_val = ir.CreateLoad(
+ casted_rsp, llvm::Type::getIntNTy(*gContext, ptr_size));
auto comparison = ir.CreateICmpEQ(rsp_val, GetConstantInt(ptr_size, 0));
ir.CreateCondBr(comparison, is_null_block, end_block);
// Stack pointer is pointing at nothing, so we need to set it up
ir.SetInsertPoint(is_null_block);
ir.CreateStore(InitialStackPointerValue(), casted_rsp);
+
+ // Store the address of `__mcsema_tls` into the TLS register.
+ if (auto tp_name = ThreadPointerName(); tp_name) {
+ if (auto tp_reg = gArch->RegisterByName(tp_name); tp_reg) {
+ ir.CreateStore(
+ InitialThreadLocalStorage(),
+ tp_reg->AddressOf(reg_state, is_null_block));
+ }
+ }
+
+ ir.SetInsertPoint(is_null_block);
+
+ // Call the `__mcsema_early_init` function to make sure all lazy cross-
+ // reference initializers have been installed before any lifted bitcode
+ // is executed.
+ ir.CreateCall(GetOrCreateMcSemaInitializer());
+
ir.CreateBr(end_block);
// Last block just returns void
ir.SetInsertPoint(end_block);
- ir.CreateRetVoid();
+ ir.CreateRet(reg_state);
return func;
}
+// TODO(lukas): VerifyRegState is probably not the best name.
+// Maybe VerifyStackPointer?
+// Opened to suggestions.
+
// Because of possible parallelism, both global stack and state must be
// thread_local. However after new thread is created, its stack and state
// are initialized to default values.
// Which means that state is zero initialized
// This function verifies that the stack pointer points to some location
// and if not then sets it up to point into stack with default offset
-llvm::Function *GetVerifyRegState(void) {
+static llvm::Function *GetVerifyRegState(void) {
static llvm::Function *func = nullptr;
if (!func) {
func = CreateVerifyRegState();
@@ -391,163 +461,190 @@ llvm::Function *GetVerifyRegState(void) {
return func;
}
-// Inserts call to __mcsema_verify_reg_state as first instruction in function
-void InsertVerifyFunction(llvm::Function *func) {
- auto &first_inst = func->front().front();
- auto verify_func = GetVerifyRegState();
- llvm::CallInst::Create(verify_func, {}, "", &first_inst);
-}
-
// Implements a stub for an externally defined function in such a way that
// the external is explicitly called, and arguments from the modeled CPU
// state are passed into the external.
static llvm::Function *ImplementExplicitArgsEntryPoint(
- const NativeObject *cfg_func, const std::string &name) {
+ const NativeFunction *cfg_func, const std::string &name) {
- auto num_args = FLAGS_explicit_args_count;
- if (name == "main") {
- num_args = 3;
- }
+ auto func = gModule->getFunction(name);
+ if (!func) {
+ auto num_args = FLAGS_explicit_args_count;
- // These function needs to be created with 0 arguments, otherwise
- // lift could crash when using both --explicit_args
- // and --libc_constructor (issue #424)
- const static std::array zero_argument_functions = {{
- "__libc_csu_init",
- "__libc_csu_fini",
- "init",
- "fini"
- }};
-
- for (const auto &zero_func_name : zero_argument_functions) {
- if (cfg_func->name == zero_func_name) {
- num_args = 0;
- break;
+ // Get correct return type -> i32 for main
+ llvm::Type *ret_type = gWordType;
+ if (name == "main" || name == "_main") {
+ num_args = 3;
+ ret_type = llvm::Type::getInt32Ty(*gContext);
}
+
+ LOG(INFO)
+ << "Generating explicit argument entrypoint function for "
+ << name << ", calling into " << cfg_func->lifted_name;
+
+ std::vector arg_types(num_args, gWordType);
+
+ auto func_type = llvm::FunctionType::get(ret_type, arg_types, false);
+ func = llvm::Function::Create(
+ func_type, llvm::GlobalValue::InternalLinkage, name, gModule.get());
+ DCHECK_EQ(func->getName().str(), name);
}
- // Get correct return type -> i32 for main
- auto ret_type = [&]() -> llvm::Type* {
- if (name == "main") {
- return llvm::Type::getInt32Ty(*gContext);
- }
- return remill::AddressType(gModule.get());
- }();
-
- // The the lifted function type so that we can get things like the memory
- // pointer type and stuff.
- auto bb = remill::BasicBlockFunction(gModule.get());
- auto state_ptr_arg = remill::NthArgument(bb, remill::kStatePointerArgNum);
- auto mem_ptr_arg = remill::NthArgument(bb, remill::kMemoryPointerArgNum);
- auto pc_arg = remill::NthArgument(bb, remill::kPCArgNum);
- auto pc_type = pc_arg->getType();
-
- LOG(INFO)
- << "Generating explicit argument entrypoint function for "
- << name << ", calling into " << cfg_func->lifted_name;
-
- std::vector arg_types(num_args, pc_type);
-
- auto func_type = llvm::FunctionType::get(ret_type, arg_types, false);
- auto func = llvm::Function::Create(
- func_type, llvm::GlobalValue::InternalLinkage, name, gModule.get());
-
- remill::ValueMap value_map;
- value_map[mem_ptr_arg] = llvm::Constant::getNullValue(mem_ptr_arg->getType());
- value_map[pc_arg] = llvm::ConstantInt::get(pc_type, cfg_func->ea);
- value_map[state_ptr_arg] = GetStatePointer();
-
- remill::CloneFunctionInto(bb, func, value_map);
-
- // NOTE(pag): Some versions of LLVM use `AttributeSet`, while others
- // use `AttributeList`.
- decltype(func->getAttributes()) attr_set_or_list;
- func->setAttributes(attr_set_or_list);
- func->addFnAttr(llvm::Attribute::NoInline);
- func->addFnAttr(llvm::Attribute::NoBuiltin);
-
- InsertVerifyFunction(func);
-
- if (FLAGS_stack_protector) {
- func->addFnAttr(llvm::Attribute::StackProtectReq);
+ if (!func->isDeclaration()) {
+ return func;
}
- // Remove the `return` in code cloned from `__remill_basic_block`.
- auto block = &(func->front());
- auto term = block->getTerminator();
- term->eraseFromParent();
+ auto maybe_decl = anvill::FunctionDecl::Create(*func, gArch);
+ if (remill::IsError(maybe_decl)) {
+ LOG(FATAL)
+ << remill::GetErrorString(maybe_decl);
+ }
- CallingConvention loader(gArch->DefaultCallingConv());
+ auto &decl = remill::GetReference(maybe_decl);
+ decl.address = cfg_func->ea;
- loader.StoreThreadPointer(block, InitialThreadLocalStorage());
+ // We have the decompiled function, or at least, a prefix of it,
+ // so we'll invent a state structure and a stack frame and we'll
+ // call the lifted function with that. The lifted function will
+ // get inlined into this function.
- // Save off the old stack pointer for later.
- auto old_sp = loader.LoadStackPointer(block);
+ auto block = llvm::BasicBlock::Create(*gContext, "", func);
+ llvm::IRBuilder<> ir(block);
- // Call the `__mcsema_early_init` function to make sure all lazy cross-
- // reference initializers have been installed before any lifted bitcode
- // is executed.
- llvm::CallInst::Create(GetOrCreateMcSemaInitializer(), "", block);
+ // Invent a memory pointer.
+ const auto mem_ptr_type = remill::MemoryPointerType(gModule.get());
+ llvm::Value *mem_ptr = llvm::Constant::getNullValue(mem_ptr_type);
- // Send in argument values.
- std::vector explicit_args;
+ const auto state_ptr = ir.CreateCall(GetVerifyRegState());
+
+ const auto pc = llvm::ConstantInt::get(gWordType, cfg_func->ea);
+
+ static remill::IntrinsicTable intrinsics(gModule);
+
+ // Store the function parameters either into the state struct
+ // or into memory (likely the stack).
+ auto arg_index = 0u;
for (auto &arg : func->args()) {
- explicit_args.push_back(&arg);
+ const auto ¶m_decl = decl.params[arg_index++];
+ mem_ptr = anvill::StoreNativeValue(
+ &arg, param_decl, intrinsics,
+ block, state_ptr, mem_ptr);
}
- loader.StoreArguments(block, explicit_args);
- // Allocate any space needed on the stack for a return address.
- loader.AllocateReturnAddress(block);
+ llvm::Value *lifted_func_args[remill::kNumBlockArgs] = {};
+ lifted_func_args[remill::kStatePointerArgNum] = state_ptr;
+ lifted_func_args[remill::kMemoryPointerArgNum] = mem_ptr;
+ lifted_func_args[remill::kPCArgNum] = pc;
+ mem_ptr = ir.CreateCall(cfg_func->lifted_function, lifted_func_args);
- // Call the lifted function.
- std::vector args(3);
- args[remill::kMemoryPointerArgNum] = remill::LoadMemoryPointer(block);
- args[remill::kStatePointerArgNum] = value_map[state_ptr_arg];
- args[remill::kPCArgNum] = value_map[pc_arg];
+ llvm::Value *ret_val = nullptr;
- llvm::CallInst::Create(
- gModule->getFunction(cfg_func->lifted_name), args, "", block);
+ if (decl.returns.size() == 1) {
+ ret_val = anvill::LoadLiftedValue(
+ decl.returns.front(), intrinsics, block,
+ state_ptr, mem_ptr);
+ ir.SetInsertPoint(block);
- // Restore the old stack pointer.
- loader.StoreStackPointer(block, old_sp);
+ } else if (1 < decl.returns.size()){
+ ret_val = llvm::UndefValue::get(func->getReturnType());
+ auto index = 0u;
+ for (auto &ret_decl : decl.returns) {
+ auto partial_ret_val = anvill::LoadLiftedValue(
+ ret_decl, intrinsics, block,
+ state_ptr, mem_ptr);
+ ir.SetInsertPoint(block);
+ unsigned indexes[] = {index};
+ ret_val = ir.CreateInsertValue(
+ ret_val, partial_ret_val, indexes);
+ index += 1;
+ }
+ }
- // Extract and return the return value from the state structure/memory.
- auto ret = loader.LoadReturnValue(block, pc_type);
- auto casted = llvm::CastInst::CreateTruncOrBitCast(
- ret, func_type->getReturnType(), "", block);
- llvm::ReturnInst::Create(*gContext, casted, block);
-
- if (!FLAGS_pc_annotation.empty()) {
- legacy::AnnotateInsts(func, cfg_func->ea);
+ if (ret_val) {
+ ir.CreateRet(ret_val);
+ } else {
+ ir.CreateRetVoid();
}
return func;
}
-static llvm::Function *GetOrCreateCallback(const NativeObject *cfg_func,
+static llvm::Function *GetOrCreateCallback(const NativeFunction *cfg_func,
const std::string &callback_name) {
- CHECK(!cfg_func->is_external)
- << "Cannot get entry point thunk for external function "
- << cfg_func->name;
-
- auto callback_func = gModule->getFunction(callback_name);
- if (callback_func) {
- return callback_func;
+ if (cfg_func->function) {
+ return cfg_func->function;
}
+ CHECK_NOTNULL(cfg_func->lifted_function);
+
if (FLAGS_explicit_args) {
- return ImplementExplicitArgsEntryPoint(cfg_func, callback_name);
+ cfg_func->function = ImplementExplicitArgsEntryPoint(
+ cfg_func, callback_name);
} else {
- return ImplementNativeToLiftedCallback(cfg_func, callback_name);
+ cfg_func->function = ImplementNativeToLiftedCallback(
+ cfg_func, callback_name);
}
+
+ return cfg_func->function;
+}
+
+// Adapt a variadic function type to have the same signature, but have additional
+// explicit "padding" arguments so that the function has at least `num_args`
+// parameters.
+static llvm::FunctionType *AdaptFunctionType(
+ llvm::FunctionType *type, unsigned num_args) {
+ std::vector param_types(
+ std::max(num_args, type->getNumParams()), gWordType);
+
+ auto i = 0u;
+ for (auto param_type : type->params()) {
+ param_types[i++] = param_type;
+ }
+
+ return llvm::FunctionType::get(
+ type->getReturnType(), param_types, false);
+}
+
+// If `va_func` is not variadic, then this returns `va_func`, otherwise
+// `va_func` is wrapped with a function that may take in additional explicit
+// integer arguments, and then will pass those to `va_func`.
+static llvm::Function *WrapVarArgsFunction(llvm::Function *va_func) {
+ if (!va_func->isVarArg()) {
+ return va_func;
+ }
+
+ auto func_type = AdaptFunctionType(
+ va_func->getFunctionType(), FLAGS_explicit_args_count);
+
+ auto wrapper_function = llvm::Function::Create(
+ func_type,
+ llvm::GlobalValue::InternalLinkage,
+ va_func->getName() + "_novarargs", gModule.get());
+
+ std::vector params;
+ for (auto &arg : wrapper_function->args()) {
+ params.push_back(&arg);
+ }
+
+ llvm::IRBuilder<> ir(
+ llvm::BasicBlock::Create(*gContext, "", wrapper_function));
+ auto call = ir.CreateCall(va_func, params);
+ if (func_type->getReturnType()->isVoidTy()) {
+ ir.CreateRetVoid();
+ } else {
+ ir.CreateRet(call);
+ }
+
+ wrapper_function->setCallingConv(va_func->getCallingConv());
+
+ return wrapper_function;
}
// Implements a stub for an externally defined function in such a way that,
// when executed, this stub redirects control flow into the actual external
// function.
static void ImplementLiftedToNativeCallback(
- llvm::Function *callback_func, llvm::Function *extern_func,
- const NativeExternalFunction *cfg_func) {
+ llvm::Function *callback_func, llvm::Function *extern_func) {
callback_func->addFnAttr(llvm::Attribute::NoInline);
@@ -571,141 +668,137 @@ static void ImplementLiftedToNativeCallback(
// the external is explicitly called, and arguments from the modeled CPU
// state are passed into the external.
static void ImplementExplicitArgsExitPoint(
- llvm::Function *callback_func, llvm::Function *extern_func,
- const NativeExternalFunction *cfg_func) {
+ llvm::Function *callback_func, llvm::Function *extern_func) {
+
+ extern_func = WrapVarArgsFunction(extern_func);
+ auto maybe_decl = anvill::FunctionDecl::Create(*extern_func, gArch);
+ if (remill::IsError(maybe_decl)) {
+ LOG(FATAL)
+ << remill::GetErrorString(maybe_decl);
+ }
+
+ const auto &decl = remill::GetReference(maybe_decl);
remill::CloneBlockFunctionInto(callback_func);
+ auto block = &(callback_func->getEntryBlock());
- // Inline so that static analyses of the bitcode don't need to dive
- // into an extra function just to see the intended call.
- // With explicit_args breaks DSE, so we rather not inline it
- if (!FLAGS_explicit_args) {
- callback_func->removeFnAttr(llvm::Attribute::NoInline);
- callback_func->addFnAttr(llvm::Attribute::InlineHint);
- callback_func->addFnAttr(llvm::Attribute::AlwaysInline);
- callback_func->removeFnAttr(llvm::Attribute::NoUnwind);
- }
+ auto pc = remill::NthArgument(
+ callback_func, remill::kPCArgNum);
+ auto mem_ptr = remill::NthArgument(
+ callback_func, remill::kMemoryPointerArgNum);
+ auto state_ptr = remill::NthArgument(
+ callback_func, remill::kStatePointerArgNum);
+
+ llvm::IRBuilder<> ir(block);
+
+ llvm::Value *next_pc_ref = nullptr;
+ next_pc_ref = ir.CreateAlloca(
+ gWordType, llvm::Constant::getNullValue(gWordType), "next_pc");
+ ir.CreateStore(pc, next_pc_ref);
+
+ const auto mem_ptr_ref = ir.CreateAlloca(mem_ptr->getType(), nullptr, "MEMORY");
+ const auto pc_ref = ir.CreateAlloca(gWordType, nullptr, "PC");
+ ir.CreateStore(mem_ptr, mem_ptr_ref);
+ ir.CreateStore(pc, pc_ref);
+
+ // Optimization can sometimes result in memory accesses being reordered w.r.t.
+ // external calls if they are inlined, so we want to prevent their inlining.
+ callback_func->removeFnAttr(llvm::Attribute::InlineHint);
+ callback_func->removeFnAttr(llvm::Attribute::AlwaysInline);
+ callback_func->addFnAttr(llvm::Attribute::NoInline);
if (FLAGS_stack_protector) {
callback_func->addFnAttr(llvm::Attribute::StackProtectReq);
}
- LOG(INFO)
- << "Generating " << cfg_func->num_args
- << " argument getters in function "
- << cfg_func->lifted_name << " for external " << cfg_func->name;
+ callback_func->setLinkage(llvm::GlobalValue::InternalLinkage);
- auto func_type = extern_func->getFunctionType();
- auto num_params = func_type->getNumParams();
- CallingConvention loader(cfg_func->cc);
+ remill::IntrinsicTable intrinsics(gModule);
+ const auto new_mem_ptr = decl.CallFromLiftedBlock(
+ extern_func->getName().str(),
+ intrinsics, block, state_ptr, mem_ptr, true);
- if (num_params > cfg_func->num_args) {
- LOG(ERROR)
- << "Function " << cfg_func->name << " may be incorrectly "
- << "specified in the CFG with " << cfg_func->num_args
- << " whereas the bitcode function has " << num_params
- << ": " << remill::LLVMThingToString(extern_func);
-
- } else if (num_params != cfg_func->num_args) {
- CHECK(num_params < cfg_func->num_args && func_type->isVarArg())
- << "Function " << remill::LLVMThingToString(extern_func)
- << " is expected to be able to take " << cfg_func->num_args
- << " arguments.";
- }
-
- auto block = &(callback_func->back());
- auto actual_num_args = std::max(num_params, cfg_func->num_args);
-
- // create call to function and args
- std::vector call_args;
- auto arg_iter = extern_func->arg_begin();
- for (auto i = 0U; i < actual_num_args; i++) {
- llvm::Type *param_type = nullptr;
- bool is_byval = false;
- if (i < num_params) {
- param_type = func_type->getParamType(i);
- is_byval = arg_iter->hasByValAttr();
- ++arg_iter;
- }
- call_args.push_back(loader.LoadNextArgument(block, param_type, is_byval));
- }
-
- // Now that we've read the argument values, we want to free up the space that
- // the emulated caller set up, so that when we eventually return, things are
- // in the expected state.
- loader.FreeReturnAddress(block);
- loader.FreeArguments(block);
-
- llvm::IRBuilder<> ir(block);
- loader.StoreReturnValue(block, ir.CreateCall(extern_func, call_args));
-
- ir.CreateRet(remill::LoadMemoryPointer(block));
+ ir.CreateRet(new_mem_ptr);
}
} // namespace
-// Get a callback function for an internal function that can be referenced by
-// internal code.
-llvm::Function *GetNativeToLiftedCallback(const NativeObject *cfg_func) {
- if (cfg_func->is_exported) {
- return GetNativeToLiftedEntryPoint(cfg_func);
- } else {
- std::stringstream ss;
- ss << "callback_" << cfg_func->lifted_name;
- return GetOrCreateCallback(cfg_func, ss.str());
+llvm::Constant *NativeFunction::Pointer(void) const {
+ if (function) {
+ return function;
}
+
+ if (name.empty()) {
+ std::stringstream ss;
+ ss << "callback_" << lifted_name;
+ name = ss.str();
+ }
+
+ module->AddNameToAddress(name, ea);
+ if (decl) {
+ decl->DeclareInModule(name, *gModule);
+ }
+
+ function = GetOrCreateCallback(this, name);
+
+ // Always set as external until we've lifted the data segments.
+ function->setLinkage(llvm::GlobalValue::ExternalLinkage);
+
+ function->removeFnAttr(llvm::Attribute::InlineHint);
+ function->removeFnAttr(llvm::Attribute::AlwaysInline);
+ function->removeFnAttr(llvm::Attribute::ReadNone);
+ function->removeFnAttr(llvm::Attribute::ReadOnly);
+ function->removeFnAttr(llvm::Attribute::ArgMemOnly);
+
+ function->addFnAttr(llvm::Attribute::NoInline);
+ function->addFnAttr(llvm::Attribute::NoBuiltin);
+
+ return function;
}
-// Get a callback function for an internal function.
-llvm::Function *GetNativeToLiftedEntryPoint(const NativeObject *cfg_func) {
- return GetOrCreateCallback(cfg_func, cfg_func->name);
+llvm::Constant *NativeFunction::Address(void) const {
+ return llvm::ConstantExpr::getPtrToInt(Pointer(), gWordType);
}
// Get a callback function for an external function that can be referenced by
// internal code.
-llvm::Function *GetLiftedToNativeExitPoint(const NativeObject *cfg_func_) {
- CHECK(cfg_func_->is_external)
- << "Cannot get exit point thunk for internal function "
- << cfg_func_->name << " at " << std::hex << cfg_func_->ea;
+llvm::Function *GetLiftedToNativeExitPoint(const NativeFunction *cfg_func) {
- auto cfg_func = reinterpret_cast(cfg_func_);
- CHECK(cfg_func->name != cfg_func->lifted_name);
-
- auto callback_func = gModule->getFunction(cfg_func->lifted_name);
- if (callback_func) {
- return callback_func;
+ if (cfg_func->callable_lifted_function) {
+ return cfg_func->callable_lifted_function;
}
- auto extern_func = gModule->getFunction(cfg_func->name);
- CHECK(extern_func != nullptr)
- << "Cannot find declaration or definition for external function "
- << cfg_func->name;
-
// Stub that will marshal lifted state into the native state.
- callback_func = llvm::Function::Create(LiftedFunctionType(),
- llvm::GlobalValue::InternalLinkage,
- cfg_func->lifted_name, gModule.get());
+ const auto callback_func = llvm::Function::Create(
+ gArch->LiftedFunctionType(), llvm::GlobalValue::InternalLinkage,
+ cfg_func->lifted_name, gModule.get());
+
+ cfg_func->callable_lifted_function = callback_func;
+
+ const auto extern_func = llvm::dyn_cast(cfg_func->Pointer());
// Pass through the memory and state pointers, and pass the destination
// (native external function address) as the PC argument.
- if (FLAGS_explicit_args || cfg_func->is_explicit) {
- ImplementExplicitArgsExitPoint(callback_func, extern_func, cfg_func);
+ if (FLAGS_explicit_args) {
+ ImplementExplicitArgsExitPoint(callback_func, extern_func);
// We are going from lifted to native code. We don't need an assembly stub
// because `__remill_function_call` already does the right thing.
} else {
- ImplementLiftedToNativeCallback(callback_func, extern_func, cfg_func);
+ ImplementLiftedToNativeCallback(callback_func, extern_func);
}
- if (!FLAGS_pc_annotation.empty()) {
- legacy::AnnotateInsts(callback_func, cfg_func->ea);
+ if (cfg_func->lifted_function && !FLAGS_pc_annotation.empty()) {
+ legacy::AnnotateInsts(cfg_func->lifted_function, cfg_func->ea);
}
+
return callback_func;
}
// Get a function that goes from the current lifted state into native state,
// where we don't know where the native destination actually is.
llvm::Function *GetLiftedToNativeExitPoint(ExitPointKind kind) {
+
if (!FLAGS_explicit_args) {
switch (kind) {
case kExitPointJump:
@@ -722,43 +815,66 @@ llvm::Function *GetLiftedToNativeExitPoint(ExitPointKind kind) {
return callback_func;
}
- // Stub that will marshal lifted state into the native state.
- callback_func = llvm::Function::Create(LiftedFunctionType(),
- llvm::GlobalValue::InternalLinkage,
- "__mcsema_detach_call_value", gModule.get());
+ std::vector arg_types;
+ arg_types.insert(arg_types.end(), FLAGS_explicit_args_count, gWordType);
- remill::CloneBlockFunctionInto(callback_func);
+ // We'll create and destroy this function a few times just for the sake of
+ // being able to get/use an anvill::FunctionDecl, and later, to get a function
+ // pointer of the right type.
+ auto func = llvm::Function::Create(
+ llvm::FunctionType::get(gWordType, arg_types, false),
+ llvm::GlobalValue::InternalLinkage, "__mcsema_do_detach_call_value",
+ gModule.get());
- CallingConvention loader(gArch->DefaultCallingConv());
+ auto maybe_decl = anvill::FunctionDecl::Create(*func, gArch);
+ func->eraseFromParent();
- auto block = &(callback_func->back());
-
- std::vector call_args;
- for (uint64_t i = 0U; i < FLAGS_explicit_args_count; i++) {
- call_args.push_back(loader.LoadNextArgument(block));
+ if (remill::IsError(maybe_decl)) {
+ LOG(FATAL)
+ << "Unable to create exit point: " << remill::GetErrorString(maybe_decl);
+ return nullptr;
}
- std::vector arg_types(FLAGS_explicit_args_count,
- remill::AddressType(gModule.get()));
+ const auto &decl = remill::GetReference(maybe_decl);
+ func = decl.DeclareInModule(
+ "__mcsema_do_detach_call_value", *gModule, true);
+ CHECK_NOTNULL(func);
- auto pc = remill::LoadProgramCounter(block);
- auto func_ptr_ty = llvm::PointerType::get(
- llvm::FunctionType::get(remill::AddressType(gModule.get()), arg_types, false), 0);
+ // Stub that will marshal lifted state into the native state.
+ callback_func = llvm::Function::Create(
+ gArch->LiftedFunctionType(), llvm::GlobalValue::PrivateLinkage,
+ "__mcsema_detach_call_value", gModule.get());
+
+ remill::CloneBlockFunctionInto(callback_func);
+ auto block = &(callback_func->getEntryBlock());
+
+ auto pc = remill::NthArgument(
+ callback_func, remill::kPCArgNum);
+ auto mem_ptr = remill::NthArgument(
+ callback_func, remill::kMemoryPointerArgNum);
+ auto state_ptr = remill::NthArgument(
+ callback_func, remill::kStatePointerArgNum);
llvm::IRBuilder<> ir(block);
- loader.StoreReturnValue(
- block, ir.CreateCall(ir.CreateIntToPtr(pc, func_ptr_ty), call_args));
+ auto pc_as_func_ptr = ir.CreateIntToPtr(pc, func->getType());
- // This means that indirect call happened and caller pushed his return
- // address, which he expects callee will pop. However callee is one
- // of entrypoints/callbacks, which freeze the %rsp, so we need to pop it
- // for callee
- loader.FreeReturnAddress(block);
+ remill::IntrinsicTable intrinsics(gModule);
+ const auto new_mem_ptr = decl.CallFromLiftedBlock(
+ "__mcsema_do_detach_call_value", intrinsics, block,
+ state_ptr, mem_ptr, true);
- ir.CreateRet(remill::LoadMemoryPointer(block));
+ func->replaceAllUsesWith(pc_as_func_ptr);
+ func->eraseFromParent();
+
+ ir.CreateRet(new_mem_ptr);
remill::Annotate(callback_func);
+ callback_func->removeFnAttr(llvm::Attribute::NoInline);
+ callback_func->addFnAttr(llvm::Attribute::InlineHint);
+ callback_func->addFnAttr(llvm::Attribute::AlwaysInline);
+ callback_func->addFnAttr(llvm::Attribute::NoUnwind);
+
return callback_func;
}
diff --git a/mcsema/BC/Callback.h b/mcsema/BC/Callback.h
index d248cfb75..c59365c32 100644
--- a/mcsema/BC/Callback.h
+++ b/mcsema/BC/Callback.h
@@ -1,21 +1,21 @@
/*
- * Copyright (c) 2017 Trail of Bits, Inc.
+ * Copyright (c) 2020 Trail of Bits, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
-#ifndef MCSEMA_BC_CALLBACK_H_
-#define MCSEMA_BC_CALLBACK_H_
+#pragma once
namespace llvm {
@@ -23,19 +23,11 @@ class Function;
} // namespace llvm
namespace mcsema {
-struct NativeObject;
-
-// Get a callback function for an internal function that can be referenced by
-// internal code.
-llvm::Function *GetNativeToLiftedCallback(const NativeObject *cfg_func);
-
-// Get a callback function for an internal function that can be referenced by
-// external code.
-llvm::Function *GetNativeToLiftedEntryPoint(const NativeObject *cfg_func);
+struct NativeFunction;
// Get a callback function for an external function that can be referenced by
// internal code.
-llvm::Function *GetLiftedToNativeExitPoint(const NativeObject *cfg_func);
+llvm::Function *GetLiftedToNativeExitPoint(const NativeFunction *cfg_func);
enum ExitPointKind {
kExitPointJump,
@@ -47,5 +39,3 @@ enum ExitPointKind {
llvm::Function *GetLiftedToNativeExitPoint(ExitPointKind);
} // namespace mcsema
-
-#endif // MCSEMA_BC_CALLBACK_H_
diff --git a/mcsema/BC/External.cpp b/mcsema/BC/External.cpp
index baa4aa7e5..cbe285c8a 100644
--- a/mcsema/BC/External.cpp
+++ b/mcsema/BC/External.cpp
@@ -1,20 +1,24 @@
/*
- * Copyright (c) 2017 Trail of Bits, Inc.
+ * Copyright (c) 2020 Trail of Bits, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
+#include "mcsema/BC/External.h"
+
#include
+#include
#include
#include
@@ -24,144 +28,97 @@
#include
#include
-#include "remill/Arch/Arch.h"
-#include "remill/Arch/Name.h"
-#include "remill/BC/Annotate.h"
+#include
+
+#include
+#include
+#include
#include "mcsema/Arch/Arch.h"
#include "mcsema/BC/Callback.h"
-#include "mcsema/BC/External.h"
#include "mcsema/BC/Util.h"
#include "mcsema/CFG/CFG.h"
+DECLARE_bool(explicit_args);
+
namespace mcsema {
-namespace {
-// For an external named `external`, return a function with the prototype
-// `uintptr_t external(uintptr_t arg0, uintptr_t arg1, ...);`.
-//
-// TODO(pag,car,artem): Handle floating point types eventually.
-static void DeclareExternal(
- const NativeExternalFunction *cfg_func) {
-
- std::vector tys(cfg_func->num_args, gWordType);
-
- auto extfun = llvm::Function::Create(
- llvm::FunctionType::get(gWordType, tys, false),
- llvm::GlobalValue::ExternalLinkage,
- cfg_func->name, gModule.get());
-
- if (cfg_func->is_weak) {
- extfun->setLinkage(llvm::GlobalValue::ExternalWeakLinkage);
- }
-
- extfun->setCallingConv(cfg_func->cc);
- extfun->addFnAttr(llvm::Attribute::NoInline);
-
- remill::Annotate(extfun);
-}
-
-static llvm::GlobalValue::ThreadLocalMode ThreadLocalMode(
- const NativeObject *cfg_obj) {
- if (cfg_obj->is_thread_local) {
- return llvm::GlobalValue::GeneralDynamicTLSModel;
- } else {
- return llvm::GlobalValue::NotThreadLocal;
- }
-}
-
-} // namespace
-
-// Declare external functions.
-void DeclareExternals(const NativeModule *cfg_module) {
- for (const auto &entry : cfg_module->name_to_extern_func) {
- auto cfg_func = reinterpret_cast(
- entry.second->Get());
-
- CHECK(cfg_func->is_external)
- << "Trying to declare function " << cfg_func->name << " as external.";
-
- CHECK_NE(cfg_func->name, cfg_func->lifted_name);
-
- // The "actual" external function.
- if (!gModule->getFunction(cfg_func->name)) {
- LOG(INFO)
- << "Adding external function " << cfg_func->name;
- DeclareExternal(cfg_func);
+llvm::Constant *NativeExternalFunction::Pointer(void) const {
+ const auto prev_function = function;
+ function = gModule->getFunction(name);
+ if (!function && decl) {
+ function = decl->DeclareInModule(name, *gModule);
+ if (!prev_function) {
+ module->AddNameToAddress(name, ea);
}
}
- // Declare external variables.
- for (const auto &entry : cfg_module->name_to_extern_var) {
- auto cfg_var = reinterpret_cast(
- entry.second->Get());
+ if (function) {
- auto ll_var = gModule->getGlobalVariable(cfg_var->name,
- true /* AllowInternal */);
- if (!ll_var) {
- LOG(INFO)
- << "Adding external variable " << cfg_var->name;
+ if (function == prev_function) {
+ return prev_function;
+ }
- llvm::Type* var_type = nullptr;
-
- CHECK_NE(0, cfg_var->size)
- << "The size of the external variable ["
- << cfg_var->name << "] cannot be zero";
-
- // Handle external variables of up to 128 bits as intgers
- // Anything else is treated as an array of bytes
- switch(cfg_var->size) {
- case 0:
- // Why is this zero length? This should never happen
- // Attempt a fix and output a warning
- LOG(ERROR)
- << "The variable [" << cfg_var->name
- << "] has size of zero. Assuming it should be size 1";
- var_type = llvm::Type::getInt8Ty(*gContext);
- break;
- case 1: // 8 bit integer
- case 2: // 16 bit integer
- case 4: // 32 bit integer
- case 8: // 64 bit integer
- case 16: // 128 bit integer
- var_type = llvm::Type::getIntNTy(
- *gContext, static_cast(cfg_var->size * 8));
- break;
-
- // An array of bytes
- default: {
- auto byte_type = llvm::Type::getInt8Ty(*gContext);
- var_type = llvm::ArrayType::get(byte_type, static_cast(cfg_var->size));
- break;
- }
+ // The function exists and isn't varargs; use it.
+ if (is_weak) {
+ if (function->isDeclaration()) {
+ function->setLinkage(llvm::GlobalValue::ExternalWeakLinkage);
+ } else {
+ function->setLinkage(llvm::GlobalValue::AvailableExternallyLinkage);
}
-
- auto linkage = llvm::GlobalValue::ExternalLinkage;
- ll_var = new llvm::GlobalVariable(*gModule, var_type, false,
- linkage, nullptr, cfg_var->name,
- nullptr, ThreadLocalMode(cfg_var));
- if (cfg_var->ea) {
- ll_var->setAlignment(1 << __builtin_ctzl(cfg_var->ea));
- }
-
- // This could happen if the variable is declared as a segment variable,
- // but is not exported, and there is also another exported/imported
- // global of the same name.
- //
- // TODO(pag): Another reasonable interpretation of this is to rename the
- // existing symbol so that the names don't clash.
} else {
- LOG_IF(ERROR, !ll_var->hasExternalLinkage())
- << "Variable '" << cfg_var->name
- << "' is external but was not previously declared as such";
-
- ll_var->setLinkage(llvm::GlobalValue::ExternalLinkage);
+ function->setLinkage(llvm::GlobalValue::ExternalLinkage);
}
- if (!cfg_var->address) {
- cfg_var->address = llvm::ConstantExpr::getPtrToInt(ll_var, gWordType);
+ remill::Annotate(function);
+
+ return function;
+
+ // The function doesn't exist in the module, and we need to declare it with
+ // the information we have from CFG extraction.
+ } else {
+ CHECK(is_external);
+ std::vector param_types(num_args, gWordType);
+
+ module->AddNameToAddress(name, ea);
+
+ function = llvm::Function::Create(
+ llvm::FunctionType::get(gWordType, param_types, false),
+ llvm::GlobalValue::ExternalLinkage,
+ name, gModule.get());
+
+ if (is_weak) {
+ function->setLinkage(llvm::GlobalValue::ExternalWeakLinkage);
}
+
+ function->setCallingConv(cc);
+
+ function->removeFnAttr(llvm::Attribute::InlineHint);
+ function->removeFnAttr(llvm::Attribute::AlwaysInline);
+ function->removeFnAttr(llvm::Attribute::ReadNone);
+ function->removeFnAttr(llvm::Attribute::ReadOnly);
+ function->removeFnAttr(llvm::Attribute::ArgMemOnly);
+
+ function->addFnAttr(llvm::Attribute::NoInline);
+ function->addFnAttr(llvm::Attribute::NoBuiltin);
+
+ remill::Annotate(function);
+ return function;
}
}
+llvm::Constant *NativeExternalVariable::Pointer(void) const {
+ CHECK(is_external);
+ CHECK_NOTNULL(segment);
+ const auto seg = segment->Get();
+ CHECK(seg->is_external);
+ CHECK_EQ(ea, seg->ea);
+ CHECK(!seg->padding);
+ return seg->Pointer();
+}
+
+llvm::Constant *NativeExternalVariable::Address(void) const {
+ return llvm::ConstantExpr::getPtrToInt(Pointer(), gWordType);
+}
+
} // namespace mcsema
diff --git a/mcsema/BC/External.h b/mcsema/BC/External.h
index cd833d982..5874a8cb2 100644
--- a/mcsema/BC/External.h
+++ b/mcsema/BC/External.h
@@ -1,21 +1,21 @@
/*
- * Copyright (c) 2017 Trail of Bits, Inc.
+ * Copyright (c) 2020 Trail of Bits, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
-#ifndef MCSEMA_BC_EXTERNAL_H_
-#define MCSEMA_BC_EXTERNAL_H_
+#pragma once
#include
@@ -30,8 +30,6 @@ namespace CallingConv {
namespace mcsema {
struct NativeModule;
-void DeclareExternals(const NativeModule *cfg_module);
+//void DeclareExternals(const NativeModule *cfg_module);
} // namespace mcsema
-
-#endif // MCSEMA_BC_EXTERNAL_H_
diff --git a/mcsema/BC/Function.cpp b/mcsema/BC/Function.cpp
index fe7c0f181..eefd3fb2c 100644
--- a/mcsema/BC/Function.cpp
+++ b/mcsema/BC/Function.cpp
@@ -1,19 +1,22 @@
/*
- * Copyright (c) 2017 Trail of Bits, Inc.
+ * Copyright (c) 2020 Trail of Bits, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
+#include "mcsema/BC/Function.h"
+
#include
#include
@@ -44,6 +47,7 @@
#include "remill/Arch/Instruction.h"
#include "remill/BC/ABI.h"
#include "remill/BC/Annotate.h"
+#include "remill/BC/Compat/Error.h"
#include "remill/BC/Compat/ScalarTransforms.h"
#include "remill/BC/IntrinsicTable.h"
#include "remill/BC/Lifter.h"
@@ -52,7 +56,6 @@
#include "mcsema/Arch/Arch.h"
#include "mcsema/BC/Callback.h"
-#include "mcsema/BC/Function.h"
#include "mcsema/BC/Instruction.h"
#include "mcsema/BC/Legacy.h"
#include "mcsema/BC/Lift.h"
@@ -63,21 +66,32 @@
DECLARE_bool(legacy_mode);
DECLARE_bool(explicit_args);
-DECLARE_bool(disable_optimizer);
-DEFINE_bool(add_reg_tracer, false,
+DEFINE_bool(add_state_tracer, false,
"Add a debug function that prints out the register state before "
"each lifted instruction execution.");
+DEFINE_bool(add_func_state_tracer, false,
+ "Add a debug function that prints out the register state before "
+ "each lifted function execution.");
+
+DEFINE_string(trace_reg_values, "",
+ "Add a debug function that prints out the specified register values "
+ "before each lifted instruction execution. The registers printed out "
+ "always include the current program counter, as well as the registers "
+ "in specified in this option (as a comma-separated list).");
+
+DEFINE_bool(add_pc_tracer, false,
+ "Add a debug function that is invoked just before every lifted "
+ "instruction, where the PC of the instruction is passed into the "
+ "tracer. This is similar to --add_reg_tracer, but it doesn't "
+ "negatively impact optimizations.");
+
DEFINE_bool(add_breakpoints, false,
"Add 'breakpoint' functions between every lifted instruction. This "
"allows one to set a breakpoint, in the lifted code, just before a "
"specific lifted instruction is executed. This is a debugging aid.");
-DEFINE_bool(check_pc_at_breakpoints, false,
- "Check whether or not the emulated program counter is correct at "
- "each injected 'breakpoint' function. This is a debugging aid.");
-
DEFINE_string(exception_personality_func, "__gxx_personality_v0",
"Add a personality function for lifting exception handling "
"routine. Assigned __gxx_personality_v0 as default for c++ ABTs.");
@@ -87,106 +101,250 @@ DEFINE_bool(stack_protector, false, "Annotate functions so that if the bitcode "
"guards will be added.");
namespace mcsema {
-
namespace {
-// Get the personality function of exception handling ABIs.
-// For libstdc++ it will be reference to `__gxx_personality_v0`
-static llvm::Function *GetPersonalityFunction(void) {
- const auto &personality_func_name = FLAGS_exception_personality_func;
-
- // The personality function is lifted as global variable. Check and erase the
- // variable before declaring it as the function.
- if (auto personality_func = gModule->getGlobalVariable(personality_func_name)) {
- personality_func->eraseFromParent();
- }
-
- auto personality_func = gModule->getFunction(personality_func_name);
- if (personality_func == nullptr) {
- personality_func = llvm::Function::Create(
- llvm::FunctionType::get(llvm::Type::getInt32Ty(*gContext), true),
- llvm::Function::ExternalLinkage, personality_func_name, gModule.get());
- }
- return personality_func;
+static llvm::Value *LoadMemoryPointer(
+ const TranslationContext &ctx, llvm::BasicBlock *block) {
+ return ctx.lifter->LoadRegValue(block, "MEMORY");
}
+static llvm::Value *LoadMemoryPointerRef(
+ const TranslationContext &ctx, llvm::BasicBlock *block) {
+ return ctx.lifter->LoadRegAddress(block, "MEMORY");
+}
+
+static llvm::Value *LoadStatePointer(
+ const TranslationContext &, llvm::BasicBlock *block) {
+ return remill::NthArgument(block->getParent(), remill::kStatePointerArgNum);
+}
+
+static llvm::Value *LoadProgramCounter(
+ const TranslationContext &ctx, llvm::BasicBlock *block) {
+ return ctx.lifter->LoadRegValue(block, "PC");
+}
+
+static llvm::Value *LoadProgramCounterRef(
+ const TranslationContext &ctx, llvm::BasicBlock *block) {
+ return ctx.lifter->LoadRegAddress(block, "PC");
+}
+
+static llvm::Value *LoadNextProgramCounter(
+ const TranslationContext &ctx, llvm::BasicBlock *block) {
+ return ctx.lifter->LoadRegValue(block, "NEXT_PC");
+}
+
+static llvm::Value *LoadNextProgramCounterRef(
+ const TranslationContext &ctx, llvm::BasicBlock *block) {
+ return ctx.lifter->LoadRegAddress(block, "NEXT_PC");
+}
+
+// Get the register tracer. This is useful when debugging, where the runtime
+// implements the register tracer in a way that prints out all general purpose
+// register names and their values before each lifted instruction. This trace
+// can help discover the source of an emulation divergence, among other things.
+static llvm::Function *GetValueTracer(void) {
+ static llvm::Function *gValueTracer = nullptr;
+ if (gValueTracer) {
+ return gValueTracer;
+ }
+
+ gValueTracer = gModule->getFunction("__mcsema_value_tracer");
+ if (gValueTracer) {
+ return gValueTracer;
+ }
+
+ gValueTracer = llvm::Function::Create(
+ gArch->LiftedFunctionType(), llvm::GlobalValue::PrivateLinkage,
+ "__mcsema_value_tracer", gModule.get());
+ gValueTracer->removeFnAttr(llvm::Attribute::NoDuplicate);
+ gValueTracer->addFnAttr(llvm::Attribute::AlwaysInline);
+ gValueTracer->addFnAttr(llvm::Attribute::InlineHint);
+ gValueTracer->addFnAttr(llvm::Attribute::ReadNone);
+ gValueTracer->removeFnAttr(llvm::Attribute::OptimizeNone);
+ gValueTracer->removeFnAttr(llvm::Attribute::NoInline);
+
+ llvm::Value *mem_ptr = remill::NthArgument(
+ gValueTracer, remill::kMemoryPointerArgNum);
+ auto printf = gModule->getFunction("__mcsema_printf");
+ auto i32_type = llvm::Type::getInt8Ty(*gContext);
+ if (!printf) {
+ auto i8_type = llvm::Type::getInt8Ty(*gContext);
+ llvm::Type *param_types_2[] = {
+ mem_ptr->getType(),
+ llvm::PointerType::get(i8_type, 0)};
+
+ printf = llvm::Function::Create(
+ llvm::FunctionType::get(mem_ptr->getType(), param_types_2, true),
+ llvm::GlobalValue::ExternalLinkage,
+ "__mcsema_printf",
+ gModule.get());
+ }
+
+ printf->addFnAttr(llvm::Attribute::ReadNone);
+
+ std::stringstream ss;
+ std::vector args;
+ args.push_back(remill::NthArgument(gValueTracer, remill::kMemoryPointerArgNum));
+ args.push_back(nullptr); // Format.
+ args.push_back(remill::NthArgument(gValueTracer, remill::kPCArgNum));
+
+ auto format = gArch->address_size == 64 ? "=%016llx " : "=%018x ";
+ auto block = llvm::BasicBlock::Create(*gContext, "", gValueTracer);
+ auto state_ptr = remill::NthArgument(
+ gValueTracer, remill::kStatePointerArgNum);
+
+ auto pc_reg = gArch->RegisterByName(gArch->ProgramCounterRegisterName());
+ ss << "pc" << format;
+
+ std::unordered_set regs;
+ std::stringstream rs;
+ rs << FLAGS_trace_reg_values;
+ for (std::string reg_name; std::getline(rs, reg_name, ',');) {
+ const auto reg = gArch->RegisterByName(reg_name);
+ if (reg->type == gWordType && reg != pc_reg) {
+ ss << reg->name << format;
+ args.push_back(
+ new llvm::LoadInst(reg->AddressOf(state_ptr, block), "", block));
+ }
+ }
+ ss << '\n';
+
+ auto format_str = llvm::ConstantDataArray::getString(*gContext, ss.str(), true);
+ auto format_var = new llvm::GlobalVariable(
+ *gModule, format_str->getType(), true, llvm::GlobalValue::InternalLinkage,
+ format_str);
+
+ llvm::Constant *indices[] = {
+ llvm::ConstantInt::getNullValue(i32_type),
+ llvm::ConstantInt::getNullValue(i32_type)};
+
+ llvm::IRBuilder<> ir(block);
+ args[1] = llvm::ConstantExpr::getInBoundsGetElementPtr(
+ format_str->getType(), format_var, indices);
+ mem_ptr = ir.CreateCall(printf, args);
+ ir.CreateRet(mem_ptr);
+
+ return gValueTracer;
+}
+
+// Get the register tracer. This is useful when debugging, where the runtime
+// implements the register tracer in a way that prints out all general purpose
+// register names and their values before each lifted instruction. This trace
+// can help discover the source of an emulation divergence, among other things.
static llvm::Function *GetRegTracer(void) {
- auto reg_tracer = gModule->getFunction("__mcsema_reg_tracer");
- if (!reg_tracer) {
- reg_tracer = llvm::Function::Create(
- LiftedFunctionType(), llvm::GlobalValue::ExternalLinkage,
- "__mcsema_reg_tracer", gModule.get());
+ static llvm::Function *gRegTracer = nullptr;
+ if (!gRegTracer) {
+ gRegTracer = gModule->getFunction("__mcsema_reg_tracer");
+ if (!gRegTracer) {
+ gRegTracer = llvm::Function::Create(
+ gArch->LiftedFunctionType(), llvm::GlobalValue::ExternalLinkage,
+ "__mcsema_reg_tracer", gModule.get());
+ gRegTracer->addFnAttr(llvm::Attribute::NoDuplicate);
+ gRegTracer->removeFnAttr(llvm::Attribute::AlwaysInline);
+ gRegTracer->removeFnAttr(llvm::Attribute::InlineHint);
+ gRegTracer->addFnAttr(llvm::Attribute::OptimizeNone);
+ gRegTracer->addFnAttr(llvm::Attribute::NoInline);
+ gRegTracer->addFnAttr(llvm::Attribute::ReadNone);
+ }
}
- return reg_tracer;
+ return gRegTracer;
}
+// Get the PC tracer for a given program counter. This function is useful when
+// integrating with KLEE. A call to this function is injected before every
+// lifted instruction. If this function is implemented as a KLEE special
+// function handler, then the second argument is the emulated program counter,
+// and a logical stack trace feature can be implemented by recording these
+// program counters into a KLEE execution state.
+static llvm::Function *GetPCTracer(uint64_t pc) {
+ static llvm::Function *gPCTracer = nullptr;
+ if (!gPCTracer) {
+ gPCTracer = gModule->getFunction("__mcsema_pc_tracer");
+ if (!gPCTracer) {
+ llvm::Type *arg_types[1] = {gWordType};
+ auto func_type = llvm::FunctionType::get(
+ llvm::Type::getVoidTy(*gContext), arg_types, false);
+ gPCTracer = llvm::Function::Create(
+ func_type, llvm::GlobalValue::ExternalLinkage,
+ "__mcsema_pc_tracer", gModule.get());
+ gPCTracer->addFnAttr(llvm::Attribute::NoDuplicate);
+ gPCTracer->removeFnAttr(llvm::Attribute::AlwaysInline);
+ gPCTracer->removeFnAttr(llvm::Attribute::InlineHint);
+ gPCTracer->addFnAttr(llvm::Attribute::OptimizeNone);
+ gPCTracer->addFnAttr(llvm::Attribute::NoInline);
+ gPCTracer->addFnAttr(llvm::Attribute::ReadNone);
+ }
+ }
+ return gPCTracer;
+}
+
+// Get the breakpoint function for a given program counter. These functions
+// are useful for debugging. For example, if you are debugging lifted bitcode,
+// and want to stop execution at the logical location where the instruction
+// at `0xf00` resides, then in a debugger, you would add your breakpoint on
+// the `breakpoint_f00` function.
static llvm::Function *GetBreakPoint(uint64_t pc) {
std::stringstream ss;
ss << "breakpoint_" << std::hex << pc;
- auto func_name = ss.str();
+ const auto func_name = ss.str();
auto func = gModule->getFunction(func_name);
if (func) {
return func;
}
+ static const auto mem_ptr_type = remill::MemoryPointerType(gModule.get());
+ llvm::Type * const params[1] = {mem_ptr_type};
+ const auto fty = llvm::FunctionType::get(mem_ptr_type, params, false);
func = llvm::Function::Create(
- LiftedFunctionType(), llvm::GlobalValue::ExternalLinkage,
+ fty, llvm::GlobalValue::ExternalLinkage,
func_name, gModule.get());
// Make sure to keep this function around (along with `ExternalLinkage`).
- func->removeFnAttr(llvm::Attribute::ReadNone);
func->addFnAttr(llvm::Attribute::OptimizeNone);
+ func->removeFnAttr(llvm::Attribute::AlwaysInline);
+ func->removeFnAttr(llvm::Attribute::InlineHint);
func->addFnAttr(llvm::Attribute::NoInline);
-
-#if LLVM_VERSION_NUMBER < LLVM_VERSION(3, 7)
- func->addFnAttr(llvm::Attribute::ReadOnly);
-#else
- func->addFnAttr(llvm::Attribute::ArgMemOnly);
-#endif
-
- auto state_ptr = remill::NthArgument(func, remill::kStatePointerArgNum);
- auto state_ptr_type = state_ptr->getType();
+ func->addFnAttr(llvm::Attribute::ReadNone);
llvm::IRBuilder<> ir(llvm::BasicBlock::Create(*gContext, "", func));
-
- if (FLAGS_check_pc_at_breakpoints) {
- auto trap = llvm::Intrinsic::getDeclaration(gModule.get(), llvm::Intrinsic::trap);
- auto are_eq = ir.CreateICmpEQ(
- remill::NthArgument(func, remill::kPCArgNum),
- llvm::ConstantInt::get(gWordType, pc));
-
- auto not_eq_bb = llvm::BasicBlock::Create(*gContext, "", func);
- auto eq_bb = llvm::BasicBlock::Create(*gContext, "", func);
- ir.CreateCondBr(are_eq, eq_bb, not_eq_bb);
-
- ir.SetInsertPoint(not_eq_bb);
- ir.CreateCall(trap);
- ir.CreateUnreachable();
-
- ir.SetInsertPoint(eq_bb);
- }
-
- // Basically some empty inline assembly that tells the compiler not to
- // optimize away the `state` pointer before each `breakpoint_XXX` function.
- auto asm_func_type = llvm::FunctionType::get(
- llvm::Type::getVoidTy(*gContext), state_ptr_type, false);
-
- auto asm_func = llvm::InlineAsm::get(
- asm_func_type, "", "*m,~{dirflag},~{fpsr},~{flags}", true);
-
- ir.CreateCall(asm_func, state_ptr);
- ir.CreateRet(remill::NthArgument(func, remill::kMemoryPointerArgNum));
+ ir.CreateRet(remill::NthArgument(func, 0));
return func;
}
-// Tries to get the lifted function beginning at `pc`.
-static llvm::Function *GetLiftedFunction(const NativeModule *cfg_module,
- uint64_t pc) {
- if (auto cfg_func = cfg_module->TryGetFunction(pc)) {
- return gModule->getFunction(cfg_func->lifted_name);
+// Get the personality function of exception handling ABIs.
+// For libstdc++ it will be reference to `__gxx_personality_v0`
+static llvm::Function *GetPersonalityFunction(void) {
+ static llvm::Function *personality_func = nullptr;
+ if (personality_func) {
+ return personality_func;
}
- return nullptr;
+
+ const auto &personality_func_name = FLAGS_exception_personality_func;
+
+ // The personality function is lifted as global variable. Check and erase the
+ // variable before declaring it as the function.
+ if (auto personality_func_var = gModule->getGlobalVariable(personality_func_name);
+ personality_func_var) {
+ if (personality_func_var->hasNUsesOrMore(1)) {
+ LOG(ERROR)
+ << "Renaming existing exception personality variable "
+ << FLAGS_exception_personality_func;
+ personality_func_var->setName(
+ FLAGS_exception_personality_func + "__original");
+ } else {
+ personality_func_var->eraseFromParent();
+ }
+ }
+
+ personality_func = gModule->getFunction(personality_func_name);
+ if (personality_func == nullptr) {
+ personality_func = llvm::Function::Create(
+ llvm::FunctionType::get(llvm::Type::getInt32Ty(*gContext), true),
+ llvm::Function::ExternalLinkage, personality_func_name, gModule.get());
+ }
+
+ return personality_func;
}
// The exception handling prologue function clears the stack and set the
@@ -223,24 +381,24 @@ static llvm::Function *GetExceptionTypeIndex(void) {
return get_index_func;
}
-// Add a call to another function, and then update the memory pointer with the
-// result of the function.
-static void InlineSubFuncCall(llvm::BasicBlock *block,
- llvm::Function *sub) {
- auto call = llvm::CallInst::Create(
- sub, remill::LiftedFunctionArgs(block), "", block);
- call->setCallingConv(sub->getCallingConv());
- auto mem_ptr = remill::LoadMemoryPointerRef(block);
- (void) new llvm::StoreInst(call, mem_ptr, block);
-}
+enum class PCValueKind {
+ kSymbolicPC,
+ kConcretePC,
+ kUndefPC
+};
// Add a invoke to another function and then update the memory pointer with the
// result of the function. It also needs to stash the stack and frame pointer which
// can be restored before handling the exception handler.
-static void InlineSubFuncInvoke(llvm::BasicBlock *block,
- llvm::Function *sub, llvm::BasicBlock *if_normal,
- llvm::BasicBlock *if_exception,
- const NativeFunction *cfg_func) {
+static void InlineSubFuncInvoke(
+ const TranslationContext &ctx,
+ llvm::BasicBlock *block,
+ llvm::Function *sub,
+ llvm::BasicBlock *if_normal,
+ llvm::BasicBlock *if_exception,
+ const NativeFunction *cfg_func,
+ PCValueKind pc_kind=PCValueKind::kSymbolicPC) {
+
llvm::IRBuilder <> ir(block);
auto get_sp_func = gModule->getFunction("__mcsema_get_stack_pointer");
if (!get_sp_func) {
@@ -249,8 +407,9 @@ static void InlineSubFuncInvoke(llvm::BasicBlock *block,
llvm::GlobalValue::ExternalLinkage,
"__mcsema_get_stack_pointer", gModule.get());
}
+
auto sp_var = ir.CreateCall(get_sp_func);
- ir.CreateStore(sp_var, cfg_func->stack_ptr_var);
+ ir.CreateStore(sp_var, ctx.stack_ptr_var);
auto get_bp_func = gModule->getFunction("__mcsema_get_frame_pointer");
if (!get_bp_func) {
@@ -259,21 +418,72 @@ static void InlineSubFuncInvoke(llvm::BasicBlock *block,
llvm::GlobalValue::ExternalLinkage,
"__mcsema_get_frame_pointer", gModule.get());
}
+
+ llvm::Value *args[remill::kNumBlockArgs];
+ args[remill::kMemoryPointerArgNum] = LoadMemoryPointer(ctx, block);
+ args[remill::kStatePointerArgNum] = LoadStatePointer(ctx, block);
+
+ if (PCValueKind::kSymbolicPC == pc_kind) {
+ args[remill::kPCArgNum] = LoadNextProgramCounter(ctx, block);
+ } else if (PCValueKind::kUndefPC == pc_kind) {
+ args[remill::kPCArgNum] = llvm::UndefValue::get(gWordType);
+ } else {
+ args[remill::kPCArgNum] = llvm::ConstantInt::get(gWordType, ctx.inst.pc);
+ }
+
auto bp_var = ir.CreateCall(get_bp_func);
- ir.CreateStore(bp_var, cfg_func->frame_ptr_var);
+ ir.CreateStore(bp_var, ctx.frame_ptr_var);
auto invoke = ir.CreateInvoke(
- sub, if_normal, if_exception, remill::LiftedFunctionArgs(block), "");
+ sub, if_normal, if_exception, args, "");
invoke->setCallingConv(sub->getCallingConv());
+
+ // Store the memory pointer down the normal path of the invoked function.
+ auto mem_ptr_ref = LoadMemoryPointerRef(ctx, if_normal);
+ ir.SetInsertPoint(if_normal);
+ ir.CreateStore(invoke, mem_ptr_ref);
}
-// Find an external function associated with this indirect jump.
-static llvm::Function *DevirtualizeIndirectFlow(
- const NativeFunction *cfg_func, llvm::Function *fallback) {
- if (cfg_func->is_external) {
- return GetLiftedToNativeExitPoint(cfg_func);
- } else if (auto func = gModule->getFunction(cfg_func->lifted_name)) {
- return func;
+// Add a call to another function, and then update the memory pointer with the
+// result of the function.
+static llvm::Value *LiftSubFuncCall(
+ const TranslationContext &ctx,
+ llvm::BasicBlock *block,
+ llvm::Function *sub,
+ PCValueKind pc_kind=PCValueKind::kSymbolicPC,
+ uint64_t concrete_pc=0) {
+ llvm::Value *args[remill::kNumBlockArgs];
+ args[remill::kMemoryPointerArgNum] = LoadMemoryPointer(ctx, block);
+ args[remill::kStatePointerArgNum] = LoadStatePointer(ctx, block);
+
+ if (PCValueKind::kSymbolicPC == pc_kind) {
+ args[remill::kPCArgNum] = LoadNextProgramCounter(ctx, block);
+ } else if (PCValueKind::kUndefPC == pc_kind) {
+ args[remill::kPCArgNum] = llvm::UndefValue::get(gWordType);
+ } else if (concrete_pc) {
+ args[remill::kPCArgNum] = llvm::ConstantInt::get(gWordType, concrete_pc);
} else {
+ args[remill::kPCArgNum] = llvm::ConstantInt::get(gWordType, ctx.inst.pc);
+ }
+
+ auto call = llvm::CallInst::Create(sub, args, "", block);
+ call->setCallingConv(sub->getCallingConv());
+ auto mem_ptr = LoadMemoryPointerRef(ctx, block);
+ (void) new llvm::StoreInst(call, mem_ptr, block);
+ return call;
+}
+
+// Wrap the lifted function `cfg_func` with its type specification, if any, and
+// provide a lifted interface to it.
+static llvm::Function *CallableLiftedFunc(
+ const NativeFunction *cfg_func, llvm::Function *fallback) {
+
+ if (cfg_func->callable_lifted_function) {
+ return cfg_func->callable_lifted_function;
+
+ } else {
+ LOG(ERROR)
+ << "Falling back on " << cfg_func->name;
+ cfg_func->callable_lifted_function = fallback;
return fallback;
}
}
@@ -281,34 +491,14 @@ static llvm::Function *DevirtualizeIndirectFlow(
// Find an external function associated with this indirect jump.
static llvm::Function *DevirtualizeIndirectFlow(
TranslationContext &ctx, llvm::Function *fallback) {
- if (ctx.cfg_inst->flow) {
- if (auto cfg_func = ctx.cfg_inst->flow->func) {
- return DevirtualizeIndirectFlow(cfg_func, fallback);
- }
+ if (!ctx.cfg_inst) {
+ return fallback;
}
- // This is a bit sketchy, but let's assume that it might be a thunk
- // (e.g in the PLT). If so, then we're doing a jump through a loaded
- // pointer, where the pointer will be fixed up with an external EA,
- // i.e. there should be a cross-reference entry at the target pointing
- // to the destination function.
- if (auto mem = ctx.cfg_inst->mem) {
- auto seg = mem->target_segment;
-
- // In case there is a variable that has some default value, e.g. can
- // be recognized both as xref and var on the same ea
- // However later it can be changed to different value, so we can't
- // devirtualize it (issue #474)
- if (!mem->segment->is_read_only || seg == nullptr) {
- return fallback;
- }
- auto target_ea_ptr_ea = mem->target_ea;
- auto entry_it = seg->entries.find(target_ea_ptr_ea);
- if (entry_it != seg->entries.end()) {
- const NativeSegment::Entry &entry = entry_it->second;
- if (entry.xref && entry.xref->func) {
- return DevirtualizeIndirectFlow(entry.xref->func, fallback);
- }
+ if (const auto flow = ctx.cfg_inst->flow; flow) {
+ if (auto cfg_func = ctx.cfg_module->TryGetFunction(flow->target_ea);
+ cfg_func) {
+ return CallableLiftedFunc(cfg_func, fallback);
}
}
@@ -320,80 +510,72 @@ static llvm::Function *DevirtualizeIndirectFlow(
// function calls internal to a binary. However, if the function is actually
// an external function then we try to return the external version of the
// function.
-static llvm::Function *FindFunction(TranslationContext &ctx,
- uint64_t target_pc) {
+static std::pair
+FindFunction(TranslationContext &ctx, uint64_t target_pc) {
const NativeFunction *cfg_func = nullptr;
- if (ctx.cfg_inst->flow) {
- cfg_func = ctx.cfg_inst->flow->func;
- if (cfg_func && cfg_func->is_external) {
- return GetLiftedToNativeExitPoint(cfg_func);
+ if (ctx.cfg_inst) {
+ if (auto flow = ctx.cfg_inst->flow; flow) {
+ cfg_func = ctx.cfg_module->TryGetFunction(flow->target_ea);
+ if (cfg_func) {
+ return {
+ cfg_func,
+ CallableLiftedFunc(cfg_func, ctx.lifter->intrinsics->function_call)};
+ }
}
}
- auto func = GetLiftedFunction(ctx.cfg_module, target_pc);
- if (func) {
- return func;
- }
-
+ cfg_func = ctx.cfg_module->TryGetFunction(target_pc);
if (cfg_func) {
- auto lifted_func = gModule->getFunction(cfg_func->lifted_name);
- if (lifted_func) {
- LOG(WARNING)
- << "Cannot find target of instruction at " << std::hex
- << ctx.cfg_inst->ea << "; the static target "
- << std::hex << target_pc << " is not associated with a lifted"
- << " subroutine, but is associated with the function at " << std::hex
- << cfg_func->ea << " in the CFG." << std::dec;
-
- return lifted_func;
- }
+ return {cfg_func,
+ CallableLiftedFunc(cfg_func, ctx.lifter->intrinsics->function_call)};
}
- LOG(ERROR)
- << "Cannot find target of instruction at " << std::hex
- << ctx.cfg_inst->ea << "; the static target "
- << std::hex << target_pc << " is not associated with a lifted"
- << " subroutine, and it does not have a known call target."
- << std::dec;
-
- return ctx.lifter->intrinsics->error;
+ return {nullptr, nullptr};
}
-// Get the basic block within this function associated with a specific program
-// counter.
-static llvm::BasicBlock *GetOrCreateBlock(TranslationContext &ctx,
- uint64_t pc) {
- auto &block = ctx.ea_to_block[pc];
- if (!block) {
- std::stringstream ss;
- ss << "block_" << std::hex << pc;
- block = llvm::BasicBlock::Create(*gContext, ss.str(), ctx.lifted_func);
+// Try to decode an instruction.
+static bool TryDecodeInstruction(
+ TranslationContext &ctx, uint64_t pc, bool is_delayed) {
- // First, try to see if it's actually related to another function. This is
- // equivalent to a tail-call in the original code.
- if (auto tail_called_func = FindFunction(ctx, pc)) {
- LOG_IF(ERROR, !ctx.cfg_block->successor_eas.count(pc))
- << "Adding missing block " << std::hex << pc << " in function "
- << ctx.cfg_func->lifted_name << " as a tail call to "
- << tail_called_func->getName().str() << std::dec;
+ remill::Instruction &inst = is_delayed ? ctx.delayed_inst : ctx.inst;
- remill::AddTerminatingTailCall(block, tail_called_func);
+ static const auto max_inst_size = gArch->MaxInstructionSize();
+ inst.Reset();
- // Terminate the block with an unreachable inst.
- } else {
+ auto byte = ctx.cfg_module->FindByte(pc);
+ if (!byte.IsExecutable()) {
+ return false;
+ }
+
+ // Read the bytes.
+ auto &inst_bytes = inst.bytes;
+ inst_bytes.reserve(max_inst_size);
+ for (auto i = 0u; i < max_inst_size && byte && byte.IsExecutable();
+ ++i, byte = ctx.cfg_module->FindNextByte(byte)) {
+ auto maybe_val = byte.Value();
+ if (remill::IsError(maybe_val)) {
LOG(ERROR)
- << "Adding missing block " << std::hex << pc << " in function "
- << ctx.cfg_func->lifted_name << " as a jump to the error intrinsic."
- << std::dec;
-
- remill::AddTerminatingTailCall(
- block, ctx.lifter->intrinsics->missing_block);
+ << "Unable to read balue of byte at " << std::hex
+ << byte.Address() << std::dec << ": "
+ << remill::GetErrorString(maybe_val);
+ break;
+ } else {
+ inst_bytes.push_back(static_cast(remill::GetReference(maybe_val)));
}
}
- return block;
+
+ if (is_delayed) {
+ return gArch->DecodeDelayedInstruction(pc, inst.bytes, inst);
+ } else {
+ return gArch->DecodeInstruction(pc, inst.bytes, inst);
+ }
}
+static llvm::BasicBlock *GetOrCreateBlock(TranslationContext &ctx,
+ uint64_t pc,
+ bool force_as_block=false);
+
// Create a landing pad basic block.
static void CreateLandingPad(TranslationContext &ctx,
struct NativeExceptionFrame *eh_entry) {
@@ -401,7 +583,7 @@ static void CreateLandingPad(TranslationContext &ctx,
auto dword_type = llvm::Type::getInt32Ty(*gContext);
// `__mcsema_exception_ret` argument list
- std::vector args(3);
+ llvm::Value *args[3];
auto is_catch = (eh_entry->action_index != 0);
ss << "landingpad_" << std::hex << eh_entry->lp_ea;
auto landing_bb = llvm::BasicBlock::Create(
@@ -423,7 +605,7 @@ static void CreateLandingPad(TranslationContext &ctx,
lpad->setCleanup(!is_catch);
- if(is_catch) {
+ if (is_catch) {
std::stringstream g_variable_name_ss;
std::vectorarray_value_const;
auto catch_all = false;
@@ -495,26 +677,25 @@ static void CreateLandingPad(TranslationContext &ctx,
auto var_value = ir.CreateGEP(gvar_landingpad, array_index_vec);
-
#if LLVM_VERSION_NUMBER > LLVM_VERSION(3, 6)
- args[0] = ir.CreateLoad(gWordType, ctx.cfg_func->stack_ptr_var);
- args[1] = ir.CreateLoad(gWordType, ctx.cfg_func->frame_ptr_var);
+ args[0] = ir.CreateLoad(gWordType, ctx.stack_ptr_var);
+ args[1] = ir.CreateLoad(gWordType, ctx.frame_ptr_var);
args[2] = ir.CreateLoad(dword_type, var_value);
#else
- args[0] = ir.CreateLoad(ctx.cfg_func->stack_ptr_var, true);
- args[1] = ir.CreateLoad(ctx.cfg_func->frame_ptr_var, true);
+ args[0] = ir.CreateLoad(ctx.stack_ptr_var, true);
+ args[1] = ir.CreateLoad(ctx.frame_ptr_var, true);
args[2] = ir.CreateLoad(var_value, true);
#endif
} else {
auto type_index_value = ir.CreateCall(GetExceptionTypeIndex());
#if LLVM_VERSION_NUMBER > LLVM_VERSION(3, 6)
- args[0] = ir.CreateLoad(gWordType, ctx.cfg_func->stack_ptr_var);
- args[1] = ir.CreateLoad(gWordType, ctx.cfg_func->frame_ptr_var);
+ args[0] = ir.CreateLoad(gWordType, ctx.stack_ptr_var);
+ args[1] = ir.CreateLoad(gWordType, ctx.frame_ptr_var);
args[2] = ir.CreateTruncOrBitCast(type_index_value, dword_type);
#else
- args[0] = ir.CreateLoad(ctx.cfg_func->stack_ptr_var, true);
- args[1] = ir.CreateLoad(ctx.cfg_func->frame_ptr_var, true);
+ args[0] = ir.CreateLoad(ctx.stack_ptr_var, true);
+ args[1] = ir.CreateLoad(ctx.frame_ptr_var, true);
args[2] = ir.CreateTruncOrBitCast(type_index_value, dword_type);
#endif
}
@@ -536,82 +717,85 @@ static void CreateLandingPad(TranslationContext &ctx,
// Lift landing pads within the function.
static void LiftExceptionFrameLP(TranslationContext &ctx,
const NativeFunction *cfg_func) {
- if (cfg_func->eh_frame.size() > 0) {
- auto lifted_func = gModule->getFunction(cfg_func->lifted_name);
- lifted_func->addFnAttr(llvm::Attribute::UWTable);
- lifted_func->addFnAttr(llvm::Attribute::OptimizeNone);
- lifted_func->removeFnAttr(llvm::Attribute::NoUnwind);
-#if LLVM_VERSION_NUMBER > LLVM_VERSION(3, 6)
- auto personality_func = GetPersonalityFunction();
- lifted_func->setPersonalityFn(personality_func);
-#endif
+
+ if (ctx.cfg_func->eh_frame.empty()) {
+ return;
}
- for (auto &entry : cfg_func->eh_frame) {
+ const auto lifted_func = ctx.lifted_func;
+ lifted_func->addFnAttr(llvm::Attribute::OptimizeNone);
+ lifted_func->removeFnAttr(llvm::Attribute::NoUnwind);
+ lifted_func->addFnAttr(llvm::Attribute::UWTable);
+
+#if LLVM_VERSION_NUMBER > LLVM_VERSION(3, 6)
+ auto personality_func = GetPersonalityFunction();
+ lifted_func->setPersonalityFn(personality_func);
+#endif
+
+ for (const auto &entry : cfg_func->eh_frame) {
if (entry->lp_ea) {
- CreateLandingPad(ctx, entry);
+ CreateLandingPad(ctx, entry.get());
}
}
}
-// Lift both targets of a conditional branch into a branch in the bitcode,
-// where each side of the branch tail-calls to the functions associated with
-// the lifted blocks for those branch targets.
-static void LiftConditionalBranch(TranslationContext &ctx,
- llvm::BasicBlock *source,
- remill::Instruction &inst) {
- auto block_true = GetOrCreateBlock(ctx, inst.branch_taken_pc);
- auto block_false = GetOrCreateBlock(ctx, inst.branch_not_taken_pc);
- llvm::IRBuilder<> cond_ir(source);
- cond_ir.CreateCondBr(
- remill::LoadBranchTaken(source),
- block_true,
- block_false);
-}
+static void LiftFuncRestoredRegs(TranslationContext &ctx,
+ llvm::BasicBlock *block,
+ bool force=false);
// Lift an indirect jump into a switch instruction.
-static void LiftIndirectJump(TranslationContext &ctx,
- llvm::BasicBlock *block,
- remill::Instruction &inst) {
+static void LiftIndirectJump(
+ TranslationContext &ctx, llvm::BasicBlock *block,
+ const remill::Instruction &inst) {
auto exit_point = GetLiftedToNativeExitPoint(kExitPointJump);
auto fallback = DevirtualizeIndirectFlow(ctx, exit_point);
std::unordered_map block_map;
- for (auto target_ea : ctx.cfg_block->successor_eas) {
- block_map[target_ea] = GetOrCreateBlock(ctx, target_ea);
- fallback = ctx.lifter->intrinsics->missing_block;
+ if (ctx.cfg_block) {
+ for (auto target_ea : ctx.cfg_block->successor_eas) {
+ block_map.emplace(target_ea, GetOrCreateBlock(ctx, target_ea, true));
+ }
}
- if (exit_point == fallback) {
+ // If we have no targets, then a reasonable target turns out to be the
+ // next program counter (if we assume that it's a jump table.).
+ if (block_map.empty()) {
+ block_map.emplace(inst.next_pc, GetOrCreateBlock(ctx, inst.next_pc));
+ }
- // If we have no targets, then a reasonable target turns out to be the
- // next program counter (if we assume that it's a jump table.).
- if (block_map.empty()) {
- block_map[inst.next_pc] = GetOrCreateBlock(ctx, inst.next_pc);
- }
+ // Build up a set of all reachable blocks that were known at disassembly
+ // time so that we can find blocks that have no predecessors.
+ std::unordered_set succ_eas;
+ succ_eas.insert(ctx.cfg_func->ea);
+ for (auto cfg_block : ctx.cfg_func->blocks) {
+ succ_eas.insert(cfg_block->successor_eas.begin(),
+ cfg_block->successor_eas.end());
+ }
- // Build up a set of all reachable blocks that were known at disassembly
- // time so that we can find blocks that have no predecessors.
- std::unordered_set succ_eas;
- succ_eas.insert(ctx.cfg_func->ea);
- for (auto &block_entry : ctx.cfg_func->blocks) {
- auto &cfg_block = block_entry.second;
- succ_eas.insert(cfg_block->successor_eas.begin(),
- cfg_block->successor_eas.end());
- }
+ // We'll augment our block map to also target unreachable blocks, just in
+ // case our disassembly failed to find some of the targets.
+ for (auto cfg_block : ctx.cfg_func->blocks) {
+ const auto block_ea = cfg_block->ea;
+ if (block_map.count(block_ea)) {
+ continue;
- // We'll augment our block map to also target unreachable blocks, just in
- // case our disassembly failed to find some of the targets.
- for (auto &block_entry : ctx.cfg_func->blocks) {
- auto target_ea = block_entry.first;
- if (!succ_eas.count(target_ea)) {
- LOG(WARNING)
- << "Adding block " << std::hex << target_ea
- << " with no predecessors as additional target of the "
- << " indirect jump at " << inst.pc << std::dec;
- block_map[target_ea] = GetOrCreateBlock(ctx, target_ea);
- }
+ } else if (!succ_eas.count(block_ea)) {
+ LOG(WARNING)
+ << "Adding block " << std::hex << block_ea
+ << " with no predecessors as additional target of the "
+ << " indirect jump at " << inst.pc << std::dec;
+ block_map.emplace(block_ea, GetOrCreateBlock(ctx, block_ea, true));
+
+ // This block is referenced by data, so it also makes a good jump table
+ // target.
+ } else if (cfg_block->is_referenced_by_data &&
+ block_ea != ctx.cfg_func->ea) {
+ LOG(WARNING)
+ << "Adding block " << std::hex << block_ea
+ << " referenced by data as additional target of the "
+ << " indirect jump at " << inst.pc << std::dec;
+ block_map.emplace(block_ea, GetOrCreateBlock(ctx, block_ea, true));
}
}
@@ -625,16 +809,20 @@ static void LiftIndirectJump(TranslationContext &ctx,
return;
}
- // Create a "default" fall-back block for the switch.
- auto fallback_block = llvm::BasicBlock::Create(
+ // Create a "default" fall-back block for the switch. The idea here is that
+ // the xrefs might have been lifted to "block addresses" in the lifted
+ // `.text` section.
+ auto fallback_block1 = llvm::BasicBlock::Create(
*gContext, "", block->getParent());
- remill::AddTerminatingTailCall(fallback_block, fallback);
+ // Create a "default" fall-back block for the switch.
+ auto fallback_block2 = llvm::BasicBlock::Create(
+ *gContext, "", block->getParent());
auto num_blocks = static_cast(block_map.size());
- auto switch_index = remill::LoadProgramCounter(block);
+ auto switch_index = LoadProgramCounter(ctx, block);
- if (ctx.cfg_inst->offset_table) {
+ if (ctx.cfg_inst && ctx.cfg_inst->offset_table) {
LOG(INFO)
<< "Indirect jump at " << std::hex << ctx.cfg_inst->ea
<< " is a jump through an offset table with offset "
@@ -642,270 +830,634 @@ static void LiftIndirectJump(TranslationContext &ctx,
llvm::IRBuilder<> ir(block);
switch_index = ir.CreateAdd(
- ir.CreateSub(switch_index,
- LiftEA(ctx.cfg_inst->offset_table->target_segment,
- ctx.cfg_inst->offset_table->target_ea)),
+ ir.CreateSub(
+ switch_index,
+ LiftXrefInCode(ctx.cfg_inst->offset_table->target_ea)),
llvm::ConstantInt::get(
gWordType, ctx.cfg_inst->offset_table->target_ea));
- remill::StoreProgramCounter(block, switch_index);
+// // TODO(pag): Keep???
+// remill::StoreProgramCounter(block, switch_index);
}
+ // Put the `switch` into its own block. This makes handling indirect jumps
+ // with delay slots easier, because then we can treat them as direct jumps
+ // to the switch block.
+ auto switch_block = llvm::BasicBlock::Create(
+ *gContext, "", block->getParent());
auto switch_inst = llvm::SwitchInst::Create(
- switch_index, fallback_block, num_blocks, block);
+ switch_index, fallback_block1, num_blocks, switch_block);
LOG(INFO)
<< "Indirect jump at " << std::hex << inst.pc
<< " has " << std::dec << num_blocks << " targets";
// Add the cases.
- for (auto ea_to_block : block_map) {
+ std::vector fallback_block_eas;
+ std::vector fallback_funcs;
+
+ auto any_are_64_bit = false;
+
+ for (auto [ea, block] : block_map) {
+ if (ea != static_cast(ea)) {
+ any_are_64_bit = true;
+ }
+ if (ctx.cfg_module->TryGetFunction(ea)) {
+ fallback_funcs.push_back(ea);
+ } else {
+ fallback_block_eas.push_back(ea);
+ }
+
switch_inst->addCase(
- llvm::ConstantInt::get(ctx.lifter->word_type, ea_to_block.first, false),
- ea_to_block.second);
+ llvm::ConstantInt::get(ctx.lifter->word_type, ea, false), block);
}
+
+ llvm::BranchInst::Create(switch_block, block);
+
+ const auto i32_type = llvm::Type::getInt32Ty(*gContext);
+
+ if (!fallback_block_eas.empty()) {
+ std::sort(fallback_block_eas.begin(), fallback_block_eas.end());
+ const auto max_ea = fallback_block_eas.back();
+
+ auto lifted_max_ea = LiftXrefInCode(max_ea);
+ llvm::Value *block_offset = llvm::BinaryOperator::Create(
+ llvm::Instruction::Sub, lifted_max_ea, switch_index, "",
+ fallback_block1);
+
+ // The block addresses will be 64-bit values, but the lifted xrefs in the
+ // jump table may actually be 64-bit xrefs, casted down to 32 bits, so we're
+ // going to deal with truncated values to hopefully make things work.
+ if (gArch->address_size == 64 && !any_are_64_bit) {
+ block_offset = llvm::TruncInst::Create(
+ llvm::Instruction::Trunc, block_offset, i32_type, "", fallback_block1);
+ block_offset = llvm::ZExtInst::Create(
+ llvm::Instruction::ZExt, block_offset,
+ gWordType, "", fallback_block1);
+ }
+
+ // Fallback switch on lifted block addresses.
+ switch_inst = llvm::SwitchInst::Create(
+ block_offset, fallback_block2, num_blocks, fallback_block1);
+
+ for (auto [ea, block] : block_map) {
+ switch_inst->addCase(
+ llvm::ConstantInt::get(ctx.lifter->word_type, max_ea - ea, false),
+ block);
+ }
+ } else {
+ llvm::BranchInst::Create(fallback_block2, fallback_block1);
+ }
+
+ // If any of the jump targets are functions, then handle them with a sequence
+ // of comparisons.
+ while (!fallback_funcs.empty()) {
+ auto target_func_ea = fallback_funcs.back();
+ fallback_funcs.pop_back();
+
+ auto func_addr = LiftXrefInCode(target_func_ea);
+
+ // The function addresses will be 64-bit values, but the lifted xrefs in the
+ // jump table may actually be 64-bit xrefs, casted down to 32 bits, so we're
+ // going to deal with truncated values to hopefully make things work.
+ if (gArch->address_size == 64 && !any_are_64_bit) {
+ func_addr = llvm::ConstantExpr::getTrunc(func_addr, i32_type);
+ func_addr = llvm::ConstantExpr::getZExt(func_addr, gWordType);
+ }
+
+ auto cmp = llvm::CmpInst::Create(
+ llvm::Instruction::ICmp, llvm::CmpInst::ICMP_EQ,
+ switch_index, func_addr,
+ "", fallback_block2);
+
+ auto next_fallback = llvm::BasicBlock::Create(
+ *gContext, "", block->getParent());
+
+ llvm::BranchInst::Create(
+ block_map[target_func_ea],
+ next_fallback,
+ cmp,
+ fallback_block2);
+
+ fallback_block2 = next_fallback;
+ }
+
+ // This is useful for debugging, so that we can see what instruction did
+ // the indirect jump.
+ // remill::StoreProgramCounter(fallback_block2, ctx.inst.pc);
+
+ auto mem_ptr = LiftSubFuncCall(ctx, fallback_block2, fallback);
+ LiftFuncRestoredRegs(ctx, fallback_block2, true); // In case of tail-call.
+ (void) llvm::ReturnInst::Create(*gContext, mem_ptr, fallback_block2);
}
-// Returns `true` if `instr` should end a basic block and if a terminator was
-// added to end the block.
-static bool TryLiftTerminator(TranslationContext &ctx,
- llvm::BasicBlock *block,
- remill::Instruction &inst) {
- switch (inst.category) {
- case remill::Instruction::kCategoryInvalid:
- case remill::Instruction::kCategoryError:
- remill::AddTerminatingTailCall(block, ctx.lifter->intrinsics->error);
- return true;
+// Call instrumentation. These are useful for debugging.
+static void Instrument(
+ const TranslationContext &ctx, llvm::BasicBlock *block, uint64_t inst_ea) {
- case remill::Instruction::kCategoryNormal:
- case remill::Instruction::kCategoryNoOp:
- return false;
-
- case remill::Instruction::kCategoryDirectJump:
- llvm::BranchInst::Create(
- GetOrCreateBlock(ctx, inst.branch_taken_pc), block);
- return true;
-
- case remill::Instruction::kCategoryIndirectJump:
- LiftIndirectJump(ctx, block, inst);
- return true;
-
- case remill::Instruction::kCategoryDirectFunctionCall:
-
- // Treat a `call +5` as not actually needing to call out to a
- // new subroutine.
- if (inst.branch_taken_pc != inst.next_pc) {
- auto targ_func = FindFunction(ctx, inst.branch_taken_pc);
- DLOG(INFO)
- << "Function " << ctx.lifted_func->getName().str()
- << " calls " << targ_func->getName().str()
- << " at " << std::hex << inst.pc << std::dec;
- if (!ctx.cfg_inst->lp_ea) {
- InlineSubFuncCall(block, targ_func);
- } else {
- auto exception_block = ctx.lp_to_block[ctx.cfg_inst->lp_ea];
- auto normal_block = GetOrCreateBlock(ctx, inst.next_pc);
- ctx.ea_to_block[inst.next_pc] = normal_block;
- InlineSubFuncInvoke(block, targ_func, normal_block, exception_block,
- ctx.cfg_func);
- return true;
- }
-
- } else {
- LOG(WARNING)
- << "Not adding a subroutine self-call at "
- << std::hex << inst.pc << std::dec;
- }
- return false;
-
- case remill::Instruction::kCategoryIndirectFunctionCall:
- InlineSubFuncCall(
- block,
- DevirtualizeIndirectFlow(
- ctx, GetLiftedToNativeExitPoint(kExitPointFunctionCall)));
- return false;
-
- case remill::Instruction::kCategoryFunctionReturn:
- llvm::ReturnInst::Create(
- *gContext, remill::LoadMemoryPointer(block), block);
- return true;
-
- case remill::Instruction::kCategoryConditionalBranch:
- case remill::Instruction::kCategoryConditionalAsyncHyperCall:
- LiftConditionalBranch(ctx, block, inst);
- return true;
-
- case remill::Instruction::kCategoryAsyncHyperCall:
- InlineSubFuncCall(block, ctx.lifter->intrinsics->async_hyper_call);
- remill::StoreProgramCounter(block, inst.next_pc);
- return false;
- }
- return false;
-}
-
-// Lift a decoded instruction into `block`.
-static bool LiftInstIntoBlock(TranslationContext &ctx,
- llvm::BasicBlock *block,
- bool is_last) {
- remill::Instruction inst;
-
- auto inst_addr = ctx.cfg_inst->ea;
- auto &bytes = ctx.cfg_inst->bytes;
-
- if (FLAGS_legacy_mode) {
- remill::StoreProgramCounter(block, inst_addr);
+ if (!FLAGS_trace_reg_values.empty()) {
+ LiftSubFuncCall(
+ ctx, block, GetValueTracer(), PCValueKind::kConcretePC, inst_ea);
}
- if (!gArch->DecodeInstruction(inst_addr, bytes, inst)) {
- LOG(ERROR)
- << "Unable to decode instruction " << inst.Serialize()
- << " at " << std::hex << inst_addr << std::dec;
-
- remill::AddTerminatingTailCall(block, ctx.lifter->intrinsics->error);
- return false;
- }
-
- DLOG_IF(WARNING, bytes.size() != inst.NumBytes())
- << "Size of decoded instruction at " << std::hex << inst_addr
- << " (" << std::dec << inst.NumBytes()
- << ") doesn't match input instruction size ("
- << bytes.size() << ").";
-
- if (FLAGS_add_reg_tracer) {
- InlineSubFuncCall(block, GetRegTracer());
+ if (FLAGS_add_state_tracer) {
+ LiftSubFuncCall(
+ ctx, block, GetRegTracer(), PCValueKind::kConcretePC, inst_ea);
}
if (FLAGS_add_breakpoints) {
- InlineSubFuncCall(block, GetBreakPoint(inst_addr));
+ llvm::Value *args[1] = {LoadMemoryPointer(ctx, block)};
+ auto call = llvm::CallInst::Create(GetBreakPoint(inst_ea), args, "", block);
+ auto mem_ptr = LoadMemoryPointerRef(ctx, block);
+ (void) new llvm::StoreInst(call, mem_ptr, block);
}
- ctx.lifter->LiftIntoBlock(inst, block);
-
- auto ret = true;
- if (TryLiftTerminator(ctx, block, inst)) {
- if (!is_last) {
- LOG(ERROR)
- << "Ending block early at " << std::hex << inst_addr;
- ret = false;
- }
+ if (FLAGS_add_pc_tracer) {
+ auto tracer = GetPCTracer(inst_ea);
+ llvm::Value *args[1] = {llvm::ConstantInt::get(gWordType, inst_ea)};
+ (void) llvm::CallInst::Create(tracer, args, "", block);
}
+}
+
+// Lift a decoded instruction into `block`. Returns `false` if there was a
+// problem lifting.
+static void LiftInstIntoBlock(TranslationContext &ctx,
+ remill::Instruction &inst,
+ llvm::BasicBlock *block,
+ bool is_delayed) {
+ const auto inst_ea = inst.pc;
+ auto prev_cfg_inst = ctx.cfg_inst;
+ if (is_delayed) {
+ ctx.cfg_inst = ctx.cfg_module->TryGetInstruction(inst_ea);
+ }
+
+ Instrument(ctx, block, inst_ea);
+
+ // Even when something isn't supported or is invalid, we still lift
+ // a call to a semantic, e.g.`INVALID_INSTRUCTION`, so we really want
+ // to treat instruction lifting as an operation that can't fail.
+ (void) ctx.lifter->LiftIntoBlock(inst, block, is_delayed);
// Annotate every un-annotated instruction in this function with the
// program counter of the current instruction.
if (FLAGS_legacy_mode) {
- legacy::AnnotateInsts(ctx.lifted_func, inst_addr);
+ legacy::AnnotateInsts(ctx.lifted_func, inst_ea);
}
- return ret;
+ ctx.cfg_inst = prev_cfg_inst;
+}
+
+static std::unordered_map gTypeToRestorer;
+
+// Get a type-specific register restorer.
+static llvm::Function *GetRestorer(llvm::Type *type) {
+ auto &func = gTypeToRestorer[type];
+ if (func) {
+ return func;
+ }
+
+ std::stringstream ss;
+ ss << "__remill_restore." << remill::LLVMThingToString(type);
+ const auto name = ss.str();
+ func = gModule->getFunction(name);
+ if (func) {
+ return func;
+ }
+
+ llvm::Type *param_types[] = {type, type};
+ func = llvm::Function::Create(
+ llvm::FunctionType::get(type, param_types, false),
+ llvm::GlobalValue::ExternalLinkage,
+ name, gModule.get());
+
+ // Tell LLVM that this function doesn't access memory; this improves LLVM's
+ // ability to optimize around this function.
+ func->addFnAttr(llvm::Attribute::ReadNone);
+
+ return func;
+}
+
+static std::unordered_map gTypeToKiller;
+
+// Get a type-specific register killer.
+static llvm::Constant *GetKiller(llvm::Type *type) {
+ auto &gc = gTypeToKiller[type];
+ if (gc) {
+ return gc;
+ }
+
+ auto gv = gModule->getGlobalVariable("__remill_kill");
+ if (!gv) {
+ gv = new llvm::GlobalVariable(
+ *gModule, type, false, llvm::GlobalValue::ExternalLinkage,
+ nullptr, "__remill_kill");
+ }
+
+ gc = llvm::ConstantExpr::getPtrToInt(gv, type);
+ return gc;
+}
+
+
+//static std::unordered_map gTypeToSavingKiller;
+//
+//// Get a type-specific register killer that lets us still store the right
+//// value to the register.
+//static llvm::Constant *GetSavingKiller(llvm::Type *type) {
+// auto &killer = gTypeToSavingKiller[type];
+// if (killer) {
+// return killer;
+// }
+//
+// std::stringstream ss;
+// ss << "__remill_saving_kill_" << std::hex
+// << reinterpret_cast(type);
+// const auto name = ss.str();
+//
+// killer = gModule->getFunction(name);
+// if (!killer) {
+// llvm::Type *param_types[] = {type};
+// killer = llvm::Function::Create(
+// llvm::FunctionType::get(type, param_types, false),
+// llvm::GlobalValue::ExternalLinkage, name, gModule.get());
+//
+// }
+//
+// killer->addFnAttr(llvm::Attribute::ReadNone);
+//
+// return killer;
+//}
+
+// Save and restore registers around the body of a function.
+void SaveAndRestoreFunctionPreservedRegs(
+ TranslationContext &ctx, llvm::BasicBlock *entry_block,
+ llvm::BasicBlock *inst_block, llvm::Value *state_ptr,
+ uint64_t end_ea, const NativePreservedRegisters ®s) {
+
+ llvm::IRBuilder<> ir(entry_block);
+ llvm::IRBuilder<> restore_ir(inst_block);
+ for (const auto ®_name : regs.reg_names) {
+ if (!gArch->RegisterByName(reg_name)) {
+ continue;
+ }
+
+ // If it's a register in the state structure, then we're going to use
+ // a special preservation pattern. The idea is this:
+ //
+ // We will say:
+ // %reg = load %reg_ptr
+ // ...
+ // %latest_val = load %reg_ptr
+ // %restore_val = call __mcsema_restore.i64(%reg, %latest_val)
+ // store %restore_val, %reg_ptr
+ //
+ // If after optimization, we end up with the following then we can
+ // eliminate the `store` entirely.
+ // %restore_val = call __mcsema_restore.i64(%reg, %reg)
+ //
+ // However, if after optimization the two parameters don't match, then
+ // we need to preserve the restore, and we'll replace all uses of
+ // `%restore_val` with `%reg`.
+ const auto reg_ptr = ctx.lifter->LoadRegAddress(entry_block, reg_name);
+ const auto reg = ir.CreateLoad(reg_ptr);
+ const auto reg_latest = restore_ir.CreateLoad(reg_ptr);
+ llvm::Value *restorer_args[] = {reg, reg_latest};
+
+ // NOTE(pag): We use volatile stores so that LLVM doesn't eliminate them
+ // if we're saving/restoring an `alloca`d object.
+ const auto restorer = GetRestorer(reg->getType());
+ restore_ir.CreateStore(
+ restore_ir.CreateCall(restorer, restorer_args),
+ reg_ptr,
+ true /* IsVolatile */);
+
+ // Restored ones should be inserted in reverse order.
+ restore_ir.SetInsertPoint(reg_latest);
+ }
+}
+
+static void LiftDelayedInstIntoBlock(TranslationContext &ctx,
+ llvm::BasicBlock *block,
+ bool on_taken_path) {
+ if (ctx.delayed_inst.IsValid() &&
+ gArch->NextInstructionIsDelayed(ctx.inst, ctx.delayed_inst, on_taken_path)) {
+ LiftInstIntoBlock(
+ ctx, ctx.delayed_inst, block, true /* is_delayed */);
+ }
+}
+
+static void LiftSavedRegs(TranslationContext &ctx, llvm::BasicBlock *block) {
+
+ llvm::IRBuilder<> ir(block);
+ DCHECK(ctx.inst.IsFunctionCall());
+ ctx.cfg_module->ForEachInstructionPreservedRegister(
+ ctx.inst.pc,
+ [=, &ir, &ctx] (const std::string ®_name) {
+ if (const auto reg = gArch->RegisterByName(reg_name); reg) {
+ const auto reg_ptr = ctx.lifter->LoadRegAddress(block, reg_name);
+ const auto reg_val = ir.CreateLoad(reg_ptr);
+ ctx.preserved_regs.emplace_back(reg_ptr, reg_val);
+ }
+ });
+}
+
+static void LiftRestoredRegs(TranslationContext &ctx, llvm::BasicBlock *block) {
+ if (ctx.preserved_regs.empty()) {
+ return;
+ }
+ std::reverse(ctx.preserved_regs.begin(), ctx.preserved_regs.end());
+ llvm::IRBuilder<> ir(block);
+ for (auto [reg_ptr, saved_val] : ctx.preserved_regs) {
+ ir.CreateStore(saved_val, reg_ptr);
+ }
+ ctx.preserved_regs.clear();
+}
+
+void LiftFuncRestoredRegs(TranslationContext &ctx, llvm::BasicBlock *block,
+ bool force) {
+ auto regs_it = ctx.func_preserved_regs.find(ctx.inst.pc);
+ if (regs_it == ctx.func_preserved_regs.end()) {
+ if (!force || ctx.func_preserved_regs.empty()) {
+ return;
+ } else {
+ regs_it = ctx.func_preserved_regs.begin();
+ }
+ }
+
+ const auto func = block->getParent();
+ const auto state_ptr = LoadStatePointer(ctx, block);
+ SaveAndRestoreFunctionPreservedRegs(
+ ctx, &(func->getEntryBlock()), block, state_ptr,
+ ctx.inst.pc, *(regs_it->second));
+}
+
+static void KillPCAndNextPC(TranslationContext &ctx, llvm::BasicBlock *block) {
+ const auto pc_ref = LoadProgramCounterRef(ctx, block);
+ const auto npc_ref = LoadNextProgramCounterRef(ctx, block);
+ llvm::IRBuilder<> ir(block);
+ const auto kill_value = GetKiller(gWordType);
+ ir.CreateStore(kill_value, pc_ref);
+ ir.CreateStore(kill_value, npc_ref);
+}
+
+static void LiftKilledRegs(TranslationContext &ctx, llvm::BasicBlock *block) {
+ llvm::IRBuilder<> ir(block);
+ ctx.cfg_module->ForEachInstructionKilledRegister(
+ ctx.inst.pc,
+ [=, &ir, &ctx](const std::string ®_name) {
+ const auto reg_ptr = ctx.lifter->LoadRegAddress(block, reg_name);
+ if (!reg_ptr) {
+ return;
+ }
+ auto reg_type = reg_ptr->getType()->getPointerElementType();
+ ir.CreateStore(GetKiller(reg_type), reg_ptr);
+ });
+}
+
+// Get the basic block within this function associated with a specific program
+// counter.
+llvm::BasicBlock *GetOrCreateBlock(TranslationContext &ctx,
+ uint64_t pc, bool force_as_block) {
+ auto &block = ctx.ea_to_block[pc];
+ if (block) {
+ return block;
+ }
+
+ std::stringstream ss;
+ ss << "inst_" << std::hex << pc;
+ block = llvm::BasicBlock::Create(*gContext, ss.str(), ctx.lifted_func);
+
+ // Missed an instruction?! This can happen when IDA merges two instructions
+ // into one larger synthetic instruction. This might also be a tail-call.
+ ctx.work_list.emplace_back(pc, force_as_block, ctx.inst.pc);
+
+ return block;
+}
+
+// Figure out the fall-through return address for a function call. On some
+// architectures we need more logic here.
+static uint64_t FunctionReturnAddress(TranslationContext &ctx) {
+ return ctx.inst.branch_not_taken_pc;
}
// Lift a decoded block into a function.
-static void LiftBlockIntoFunction(TranslationContext &ctx) {
- auto block_pc = ctx.cfg_block->ea;
- auto block = ctx.ea_to_block[block_pc];
+static void LiftInstIntoFunction(TranslationContext &ctx,
+ llvm::BasicBlock *block) {
+ LiftInstIntoBlock(ctx, ctx.inst, block, false /* is_delayed */);
- ctx.cfg_inst = nullptr;
-
- // Lift each instruction into the block.
- size_t i = 0;
- const auto num_insts = ctx.cfg_block->instructions.size();
- for (auto &cfg_inst : ctx.cfg_block->instructions) {
- ctx.cfg_inst = cfg_inst.get();
- auto is_last = (++i) >= num_insts;
-
- if (!LiftInstIntoBlock(ctx, block, is_last)) {
- return;
- }
-
- LOG_IF(WARNING, cfg_inst->does_not_return && !is_last)
- << "Instruction at " << std::hex << cfg_inst->ea
- << " has a local no-return, but is not the last instruction"
- << " in its block (" << std::hex << block_pc
- << "). Terminating anyway." << std::dec;
+ // We might need to lift another instruction and execute it in the delay
+ // slot. `cont` contains enough info to redirect control flow after we've
+ // dealt with the lifting of the delayed instruction.
+ if (ctx.delayed_inst.IsValid()) {
+ ctx.delayed_inst.Reset();
}
- const auto &follows = ctx.cfg_block->successor_eas;
- if (!block->getTerminator()) {
- if (!ctx.cfg_inst || ctx.cfg_inst->does_not_return) {
- LOG_IF(ERROR, !ctx.cfg_inst)
- << "Block " << std::hex << block_pc << " in function "
- << ctx.cfg_func->ea << std::dec << " has no instructions; "
- << "this could be because of an incorrectly disassembled jump table.";
-
- remill::AddTerminatingTailCall(block, ctx.lifter->intrinsics->error);
-
- } else if (follows.size() == 1) {
- (void) llvm::BranchInst::Create(
- GetOrCreateBlock(ctx, *(follows.begin())), block);
-
- } else {
+ if (gArch->MayHaveDelaySlot(ctx.inst)) {
+ if (!TryDecodeInstruction(ctx, ctx.inst.delayed_pc, true) ||
+ !ctx.delayed_inst.IsValid()) {
LOG(ERROR)
- << "Block " << std::hex << block_pc << " has no terminator, and"
- << " instruction at " << std::hex << ctx.cfg_inst->ea
- << " is not a local no-return function call." << std::dec;
-
- remill::AddTerminatingTailCall(
- block, ctx.lifter->intrinsics->missing_block);
+ << "Unable to decode or use delayed instruction at " << std::hex
+ << ctx.inst.delayed_pc << std::dec << " of " << ctx.inst.Serialize();
}
}
-}
-// Allocate storage of the stack variables that we're going to lift.
-static void AllocStackVars(llvm::BasicBlock *bb,
- const NativeFunction *cfg_func) {
- llvm::IRBuilder<> ir(bb);
- llvm::Value *array_size = nullptr;
- llvm::Type *array_type = nullptr;
+ ctx.preserved_regs.clear();
- for (auto s : cfg_func->stack_vars) {
- switch(s->size){
- case 1:
- case 2:
- case 4:
- case 8:
- array_type = llvm::Type::getIntNTy(
- *gContext, static_cast(s->size * 8U));
- array_size = llvm::ConstantInt::get(gWordType, 1);
- break;
+ const auto intrinsics = ctx.lifter->intrinsics;
- default:
- array_type = llvm::Type::getInt8Ty(*gContext);
- array_size = llvm::ConstantInt::get(gWordType, s->size);
- break;
+ switch (ctx.inst.category) {
+ case remill::Instruction::kCategoryInvalid:
+ remill::AddTerminatingTailCall(block, intrinsics->error);
+ break;
+
+ case remill::Instruction::kCategoryError:
+ LiftDelayedInstIntoBlock(ctx, block, true);
+ LiftFuncRestoredRegs(ctx, block, true);
+ KillPCAndNextPC(ctx, block);
+ LiftKilledRegs(ctx, block);
+ remill::AddTerminatingTailCall(block, intrinsics->error);
+ break;
+
+ case remill::Instruction::kCategoryNormal: {
+ CHECK(!ctx.delayed_inst.IsValid());
+ llvm::BranchInst::Create(GetOrCreateBlock(ctx, ctx.inst.next_pc), block);
+ break;
}
- LOG(INFO)
- << "Inserting " << s->size << "-byte variable " << s->name
- << " into function" << cfg_func->name;
+ case remill::Instruction::kCategoryNoOp: {
+ CHECK(!ctx.delayed_inst.IsValid());
+ auto next_func = ctx.cfg_module->TryGetFunction(ctx.inst.next_pc);
+ if (next_func && (next_func->ea != ctx.cfg_func->ea)) {
+ LiftFuncRestoredRegs(ctx, block);
+ KillPCAndNextPC(ctx, block);
+ LiftKilledRegs(ctx, block);
+ llvm::ReturnInst::Create(
+ *gContext, LoadMemoryPointer(ctx, block), block);
+ } else {
+ llvm::BranchInst::Create(GetOrCreateBlock(ctx, ctx.inst.next_pc), block);
+ }
+ break;
+ }
- // TODO(kumarak): Alignment of `alloca`s?
- s->llvm_var = ir.CreateAlloca(array_type, array_size, s->name);
+ case remill::Instruction::kCategoryDirectJump:
+ LiftDelayedInstIntoBlock(ctx, block, true);
+ KillPCAndNextPC(ctx, block);
+ llvm::BranchInst::Create(
+ GetOrCreateBlock(ctx, ctx.inst.branch_taken_pc), block);
+ break;
+
+ case remill::Instruction::kCategoryIndirectJump:
+ LiftDelayedInstIntoBlock(ctx, block, true);
+ LiftIndirectJump(ctx, block, ctx.inst);
+ break;
+
+ case remill::Instruction::kCategoryDirectFunctionCall: {
+ LiftDelayedInstIntoBlock(ctx, block, true);
+
+ if (auto [targ_cfg_func, targ_func] = FindFunction(ctx, ctx.inst.branch_taken_pc);
+ targ_cfg_func && targ_func) {
+
+ if (!ctx.cfg_inst || !ctx.cfg_inst->lp_ea) {
+ LiftSavedRegs(ctx, block);
+ KillPCAndNextPC(ctx, block);
+ LiftKilledRegs(ctx, block);
+ LiftSubFuncCall(ctx, block, targ_func, PCValueKind::kUndefPC);
+ LiftRestoredRegs(ctx, block);
+ llvm::BranchInst::Create(
+ GetOrCreateBlock(ctx, FunctionReturnAddress(ctx)), block);
+
+ // TODO(pag): The save/restore optimization will produce unexpected
+ // lifts when exceptions are involved.
+ } else {
+ auto exception_block = ctx.lp_to_block[ctx.cfg_inst->lp_ea];
+ auto normal_block = GetOrCreateBlock(ctx, FunctionReturnAddress(ctx));
+ KillPCAndNextPC(ctx, block);
+ LiftKilledRegs(ctx, block);
+ InlineSubFuncInvoke(
+ ctx, block, targ_func, normal_block, exception_block,
+ ctx.cfg_func, PCValueKind::kUndefPC);
+ }
+
+ // Treat a `call +5` as not actually needing to call out to a
+ // new subroutine.
+ } else if (ctx.inst.branch_taken_pc != ctx.inst.next_pc) {
+ LOG(WARNING)
+ << "Not adding a subroutine self-call at "
+ << std::hex << ctx.inst.pc << std::dec;
+ llvm::BranchInst::Create(
+ GetOrCreateBlock(ctx, ctx.inst.branch_taken_pc), block);
+
+ // This is a legitimate call to a function that seems to have been missed.
+ } else {
+ LOG(ERROR)
+ << "Cannot find target of instruction at " << std::hex
+ << ctx.inst.pc << "; the static target "
+ << std::hex << ctx.inst.branch_taken_pc
+ << " is not associated with a lifted subroutine, and it does not "
+ << "have a known call target" << std::dec;
+
+ LiftSavedRegs(ctx, block);
+ KillPCAndNextPC(ctx, block);
+ LiftKilledRegs(ctx, block);
+ LiftSubFuncCall(ctx, block, intrinsics->function_call,
+ PCValueKind::kConcretePC, ctx.inst.branch_taken_pc);
+ LiftRestoredRegs(ctx, block);
+ llvm::BranchInst::Create(
+ GetOrCreateBlock(ctx, ctx.inst.branch_not_taken_pc), block);
+ }
+ break;
+ }
+
+ case remill::Instruction::kCategoryIndirectFunctionCall: {
+ const auto fallback_func = GetLiftedToNativeExitPoint(
+ kExitPointFunctionCall);
+ const auto target_func = DevirtualizeIndirectFlow(
+ ctx, fallback_func);
+
+ LOG_IF(ERROR, ctx.cfg_inst && ctx.cfg_inst->lp_ea)
+ << "Not treating call from " << std::hex << ctx.inst.pc
+ << " as invoke" << std::dec;
+
+ LiftDelayedInstIntoBlock(ctx, block, true);
+
+ if (!ctx.cfg_inst || !ctx.cfg_inst->lp_ea) {
+ LiftSavedRegs(ctx, block);
+ LiftKilledRegs(ctx, block);
+ if (fallback_func == target_func) {
+ LiftSubFuncCall(ctx, block, target_func);
+ } else {
+ KillPCAndNextPC(ctx, block);
+ LiftSubFuncCall(ctx, block, target_func, PCValueKind::kUndefPC);
+ }
+ LiftRestoredRegs(ctx, block);
+ llvm::BranchInst::Create(
+ GetOrCreateBlock(ctx, FunctionReturnAddress(ctx)), block);
+
+ } else {
+ auto exception_block = ctx.lp_to_block[ctx.cfg_inst->lp_ea];
+ auto normal_block = GetOrCreateBlock(ctx, FunctionReturnAddress(ctx));
+
+ if (fallback_func == target_func) {
+ InlineSubFuncInvoke(
+ ctx, block, target_func, normal_block, exception_block,
+ ctx.cfg_func);
+
+ } else {
+ KillPCAndNextPC(ctx, block);
+ LiftKilledRegs(ctx, block);
+ InlineSubFuncInvoke(
+ ctx, block, target_func, normal_block, exception_block,
+ ctx.cfg_func, PCValueKind::kUndefPC);
+ }
+ }
+ break;
+ }
+
+ case remill::Instruction::kCategoryFunctionReturn:
+ LiftDelayedInstIntoBlock(ctx, block, true);
+ LiftFuncRestoredRegs(ctx, block);
+ KillPCAndNextPC(ctx, block);
+ LiftKilledRegs(ctx, block);
+ llvm::ReturnInst::Create(
+ *gContext, LoadMemoryPointer(ctx, block), block);
+ break;
+
+ case remill::Instruction::kCategoryConditionalBranch:
+ case remill::Instruction::kCategoryConditionalAsyncHyperCall: {
+ const auto cond = remill::LoadBranchTaken(block);
+ const auto taken_block = llvm::BasicBlock::Create(
+ *gContext, "", ctx.lifted_func);
+ const auto not_taken_block = llvm::BasicBlock::Create(
+ *gContext, "", ctx.lifted_func);
+ llvm::BranchInst::Create(
+ taken_block, not_taken_block, cond, block);
+ LiftDelayedInstIntoBlock(ctx, taken_block, true);
+ LiftDelayedInstIntoBlock(ctx, not_taken_block, false);
+ llvm::BranchInst::Create(
+ GetOrCreateBlock(ctx, ctx.inst.branch_taken_pc), taken_block);
+ llvm::BranchInst::Create(
+ GetOrCreateBlock(ctx, ctx.inst.branch_not_taken_pc), not_taken_block);
+ break;
+ }
+
+ case remill::Instruction::kCategoryAsyncHyperCall:
+ LiftDelayedInstIntoBlock(ctx, block, true);
+ LiftSubFuncCall(ctx, block, ctx.lifter->intrinsics->async_hyper_call);
+ llvm::BranchInst::Create(
+ GetOrCreateBlock(ctx, ctx.inst.branch_taken_pc), block);
+ break;
}
-
- cfg_func->stack_ptr_var = ir.CreateAlloca(
- llvm::Type::getInt64Ty(*gContext),
- llvm::ConstantInt::get(gWordType, 1), "stack_ptr_var");
-
- cfg_func->frame_ptr_var = ir.CreateAlloca(
- llvm::Type::getInt64Ty(*gContext),
- llvm::ConstantInt::get(gWordType, 1), "frame_ptr_var");
}
static llvm::Function *LiftFunction(
- const NativeModule *cfg_module, const NativeFunction *cfg_func) {
+ const NativeModule *cfg_module, const NativeFunction *cfg_func,
+ const remill::IntrinsicTable &intrinsics) {
CHECK(!cfg_func->is_external)
<< "Should not lift external function " << cfg_func->name;
- static std::unique_ptr intrinsics;
- if (!intrinsics.get()) {
- intrinsics.reset(new remill::IntrinsicTable(gModule));
- }
-
- auto lifted_func = gModule->getFunction(cfg_func->lifted_name);
+ const auto lifted_func = cfg_func->lifted_function;
CHECK(nullptr != lifted_func)
<< "Could not find declaration for " << cfg_func->lifted_name;
- cfg_func->function = lifted_func;
// This can happen due to deduplication of functions during the
// CFG decoding process. In practice, though, that only really
@@ -918,30 +1470,31 @@ static llvm::Function *LiftFunction(
}
if (cfg_func->blocks.empty()) {
- LOG(ERROR)
+ LOG(WARNING)
<< "Function " << cfg_func->lifted_name << " is empty!";
- remill::AddTerminatingTailCall(lifted_func, intrinsics->missing_block);
+ remill::AddTerminatingTailCall(lifted_func, intrinsics.missing_block);
return lifted_func;
}
remill::CloneBlockFunctionInto(lifted_func);
- lifted_func->removeFnAttr(llvm::Attribute::AlwaysInline);
- lifted_func->removeFnAttr(llvm::Attribute::InlineHint);
lifted_func->removeFnAttr(llvm::Attribute::NoReturn);
lifted_func->removeFnAttr(llvm::Attribute::NoUnwind);
- lifted_func->addFnAttr(llvm::Attribute::NoInline);
lifted_func->setVisibility(llvm::GlobalValue::DefaultVisibility);
+ lifted_func->setLinkage(llvm::GlobalValue::ExternalLinkage);
+
+ lifted_func->removeFnAttr(llvm::Attribute::AlwaysInline);
+ lifted_func->removeFnAttr(llvm::Attribute::InlineHint);
+ lifted_func->addFnAttr(llvm::Attribute::NoInline);
if (FLAGS_stack_protector) {
lifted_func->addFnAttr(llvm::Attribute::StackProtectReq);
}
TranslationContext ctx;
- std::unique_ptr lifter(
- new InstructionLifter(intrinsics.get(), ctx));
+ InstructionLifter lifter(&intrinsics, ctx);
- ctx.lifter = lifter.get();
+ ctx.lifter = &lifter;
ctx.cfg_module = cfg_module;
ctx.cfg_func = cfg_func;
ctx.cfg_block = nullptr;
@@ -951,76 +1504,130 @@ static llvm::Function *LiftFunction(
std::unordered_set referenced_blocks;
referenced_blocks.insert(cfg_func->ea);
-
- // Create basic blocks for each basic block in the original function.
- for (auto &block_info : cfg_func->blocks) {
- std::stringstream ss;
- ss << "block_" << std::hex << block_info.first;
- ctx.ea_to_block[block_info.first] = llvm::BasicBlock::Create(
- *gContext, ss.str(), lifted_func);
+ // Collect the known set of blocks into a work list, and add basic blocks
+ // for each of them.
+ for (const auto cfg_block : cfg_func->blocks) {
+ const auto block_ea = cfg_block->ea;
+ (void) GetOrCreateBlock(ctx, block_ea, true /* force */);
}
- // Allocate the stack variable recovered in the function
- auto entry_block = ctx.ea_to_block[cfg_func->ea];
- AllocStackVars(entry_block, cfg_func);
+ std::sort(ctx.work_list.begin(), ctx.work_list.end(),
+ [] (std::tuple a,
+ std::tuple b) {
+ return std::get<0>(a) < std::get<0>(b);
+ });
+ auto unique_it = std::unique(ctx.work_list.begin(), ctx.work_list.end());
+ ctx.work_list.erase(unique_it, ctx.work_list.end());
// Lift the landing pad if there are exception frames recovered.
LiftExceptionFrameLP(ctx, cfg_func);
- llvm::BranchInst::Create(ctx.ea_to_block[cfg_func->ea],
- &(lifted_func->front()));
+ const auto entry_block = &(lifted_func->front());
- for (auto &block_info : cfg_func->blocks) {
- ctx.cfg_block = block_info.second.get();
- LiftBlockIntoFunction(ctx);
+ if (FLAGS_add_func_state_tracer) {
+ LiftSubFuncCall(ctx, entry_block, GetRegTracer());
}
- // Check the sanity of things.
- for (auto block_info : ctx.ea_to_block) {
- auto block = block_info.second;
- CHECK(block->getTerminator() != nullptr)
- << "Lifted block " << std::hex << block_info.first
- << " has no terminator!" << std::dec;
+ const auto next_pc_ref = LoadNextProgramCounterRef(ctx, entry_block);
+ const auto pc_ref = LoadProgramCounterRef(ctx, entry_block);
+ const auto pc = LiftXrefInCode(cfg_func->ea);
+ llvm::IRBuilder<> ir(entry_block);
+ ir.CreateStore(pc, next_pc_ref);
+ ir.CreateStore(pc, pc_ref);
+
+ // Used for exception handling.
+ ctx.stack_ptr_var = ir.CreateAlloca(
+ llvm::Type::getInt64Ty(*gContext),
+ llvm::ConstantInt::get(gWordType, 1), "stack_ptr_var");
+
+ ctx.frame_ptr_var = ir.CreateAlloca(
+ llvm::Type::getInt64Ty(*gContext),
+ llvm::ConstantInt::get(gWordType, 1), "frame_ptr_var");
+
+ // Preserve registers at a function granularity.
+ cfg_module->ForEachRangePreservedRegister(
+ cfg_func->ea,
+ [=, &ctx] (uint64_t end_ea, const NativePreservedRegisters ®s) {
+ ctx.func_preserved_regs.emplace(end_ea, ®s);
+ });
+
+ // Reverse the work list so that we can treat it like a stack.
+ std::reverse(ctx.work_list.begin(), ctx.work_list.end());
+
+ // Process the instructions in reverse order, filling up their basic blocks.
+ while (!ctx.work_list.empty()) {
+ auto [inst_ea, force_as_block, from_ea] = ctx.work_list.back();
+ ctx.work_list.pop_back();
+
+ auto block = ctx.ea_to_block[inst_ea];
+ CHECK_NOTNULL(block);
+
+ if (!block->empty()) {
+ continue; // Already handled.
+ }
+
+ // First, try to see if it's actually related to another function. This is
+ // equivalent to a tail-call in the original code.
+ if (inst_ea != cfg_func->ea) {
+ if (auto tail_called_func = cfg_module->TryGetFunction(inst_ea);
+ tail_called_func && !force_as_block) {
+ LOG(WARNING)
+ << "Adding tail-call from " << std::hex << inst_ea
+ << " in function " << ctx.cfg_func->lifted_name << " to "
+ << tail_called_func->lifted_name << " from " << from_ea << std::dec;
+
+ auto mem_ptr = LiftSubFuncCall(
+ ctx, block,
+ CallableLiftedFunc(tail_called_func,
+ ctx.lifter->intrinsics->function_call));
+ LiftFuncRestoredRegs(ctx, block, true);
+ (void) llvm::ReturnInst::Create(*gContext, mem_ptr, block);
+ continue;
+ }
+ }
+
+ if (!TryDecodeInstruction(ctx, inst_ea, false)) {
+ if (from_ea) {
+ LOG(ERROR)
+ << "Could not decode instruction at " << std::hex << inst_ea
+ << " reachable from instruction " << from_ea << " in function "
+ << cfg_func->name << " at " << cfg_func->ea
+ << std::dec << ": " << ctx.inst.Serialize();
+ } else {
+ LOG(ERROR)
+ << "Could not decode instruction at " << std::hex << inst_ea
+ << " in function " << cfg_func->name << " at " << cfg_func->ea
+ << std::dec << ": " << ctx.inst.Serialize();
+ }
+ remill::AddTerminatingTailCall(block, intrinsics.error);
+
+ } else if (!ctx.inst.IsValid() || ctx.inst.IsError()) {
+ remill::AddTerminatingTailCall(block, intrinsics.error);
+
+ } else {
+ ctx.cfg_block = ctx.cfg_module->TryGetBlock(inst_ea, ctx.cfg_block);
+ ctx.cfg_inst = ctx.cfg_module->TryGetInstruction(inst_ea);
+ LiftInstIntoFunction(ctx, block);
+ }
}
- remill::Annotate(lifted_func);
+ // Connect the function to the first block.
+ llvm::BranchInst::Create(ctx.ea_to_block[cfg_func->ea], entry_block);
+
+ // NOTE(pag): We may have unreachable blocks, especially on architectures
+ // like where instructions in delay slots might not be reachable
+ // on their own.
+
+ const auto mut_cfg_func = const_cast(cfg_func);
+ mut_cfg_func->blocks.clear();
+ mut_cfg_func->eh_frame.clear();
return lifted_func;
}
-} // namespace
-
-// Declare the lifted functions. This is a separate step from defining
-// functions because it's important that all possible code- and data-cross
-// references are resolved before any data or instructions can use
-// those references.
-void DeclareLiftedFunctions(const NativeModule *cfg_module) {
- for (auto &func : cfg_module->ea_to_func) {
- auto cfg_func = func.second->Get();
- if (cfg_func->is_external) {
- continue;
- }
-
- const auto &func_name = cfg_func->lifted_name;
- auto lifted_func = gModule->getFunction(func_name);
-
- if (!lifted_func) {
- lifted_func = remill::DeclareLiftedFunction(gModule.get(), func_name);
-
- // make local functions 'static'
- LOG(INFO)
- << "Inserted function: " << func_name;
-
- } else {
- LOG(INFO)
- << "Already inserted function: " << func_name << ", skipping.";
- }
- }
-}
-
using Calls_t = std::vector;
-bool ShouldInline(const llvm::CallSite &cs) {
+static bool ShouldInline(const llvm::CallSite &cs) {
if (!cs) {
return false;
}
@@ -1028,7 +1635,7 @@ bool ShouldInline(const llvm::CallSite &cs) {
return callee && remill::HasOriginType(callee);
}
-Calls_t InlinableCalls(llvm::Function &func) {
+static Calls_t InlinableCalls(llvm::Function &func) {
Calls_t out;
for (auto &bb : func) {
for (auto &inst : bb) {
@@ -1041,7 +1648,7 @@ Calls_t InlinableCalls(llvm::Function &func) {
return out;
}
-void InlineCalls(llvm::Function &func) {
+static void InlineCalls(llvm::Function &func) {
for (auto &cs : InlinableCalls(func)) {
if (auto call = llvm::dyn_cast(cs.getInstruction())) {
llvm::InlineFunctionInfo info;
@@ -1050,6 +1657,44 @@ void InlineCalls(llvm::Function &func) {
}
}
+} // namespace
+
+// Declare the lifted functions. This is a separate step from defining
+// functions because it's important that all possible code- and data-cross
+// references are resolved before any data or instructions can use
+// those references.
+void DeclareLiftedFunctions(const NativeModule *cfg_module) {
+
+ for (const auto [ea, cfg_func] : cfg_module->ea_to_func) {
+ (void) ea;
+
+ if (cfg_func->is_external) {
+ continue;
+ }
+
+ const auto &func_name = cfg_func->lifted_name;
+ auto lifted_func = gModule->getFunction(func_name);
+
+ if (!lifted_func) {
+ lifted_func = remill::DeclareLiftedFunction(
+ gModule.get(), func_name);
+
+ // make local functions 'static'
+ LOG(INFO)
+ << "Inserted function: " << func_name;
+
+ } else {
+ LOG(INFO)
+ << "Already inserted function: " << func_name << ", skipping.";
+ }
+
+ // All lifted functions are marked as external so they aren't optimized
+ // away.
+ lifted_func->setLinkage(llvm::GlobalValue::ExternalLinkage);
+ cfg_func->lifted_function = lifted_func;
+ }
+}
+
// Lift the blocks and instructions into the function. Some minor optimization
// passes are applied to clean out any unneeded register variables brought
// in from cloning the `__remill_basic_block` function.
@@ -1062,17 +1707,35 @@ bool DefineLiftedFunctions(const NativeModule *cfg_module) {
func_pass_manager.add(llvm::createDeadCodeEliminationPass());
func_pass_manager.doInitialization();
- for (auto &entry : cfg_module->ea_to_func) {
- auto cfg_func = reinterpret_cast(
- entry.second->Get());
+ remill::IntrinsicTable intrinsics(gModule);
+
+ // Make sure that `callable_lifted_function` is defined for each function.
+ for (auto [ea, cfg_func] : cfg_module->ea_to_func) {
+ (void) ea;
+
+ DCHECK_EQ(cfg_func, cfg_func->Get());
+
+ if (cfg_func->is_external) {
+ (void) GetLiftedToNativeExitPoint(cfg_func);
+
+ } else {
+ // For local calls between lifted functions, prefer our state-passing
+ // version, even if we generate an entry point
+ cfg_func->callable_lifted_function = cfg_func->lifted_function;
+ }
+ }
+
+ for (auto [ea, cfg_func] : cfg_module->ea_to_func) {
+ (void) ea;
+
if (cfg_func->is_external) {
continue;
}
- auto lifted_func = LiftFunction(cfg_module, cfg_func);
+ auto lifted_func = LiftFunction(cfg_module, cfg_func, intrinsics);
if (!lifted_func) {
LOG(ERROR)
- << "Could lift function: " << cfg_func->name << " at "
+ << "Could not lift function: " << cfg_func->name << " at "
<< std::hex << cfg_func->ea << " into " << cfg_func->lifted_name
<< std::dec;
return false;
@@ -1081,9 +1744,10 @@ bool DefineLiftedFunctions(const NativeModule *cfg_module) {
// Unfortunately there can be `return_twice` attribute on some external functions
// (setjmp). This prevents llvm from inlining the `ext_` wrapper, but it is mandatory
// that the actual call is behind a wrapper (due to how it is implemented).
- if (FLAGS_explicit_args && !FLAGS_disable_optimizer) {
+ if (false && FLAGS_explicit_args) {
InlineCalls(*lifted_func);
}
+
func_pass_manager.run(*lifted_func);
}
diff --git a/mcsema/BC/Function.h b/mcsema/BC/Function.h
index 4e2708f85..a7302c54f 100644
--- a/mcsema/BC/Function.h
+++ b/mcsema/BC/Function.h
@@ -1,21 +1,21 @@
/*
- * Copyright (c) 2017 Trail of Bits, Inc.
+ * Copyright (c) 2020 Trail of Bits, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
-#ifndef MCSEMA_BC_FUNCTION_H_
-#define MCSEMA_BC_FUNCTION_H_
+#pragma once
namespace mcsema {
struct NativeModule;
@@ -25,5 +25,3 @@ void DeclareLiftedFunctions(const NativeModule *cfg_module);
bool DefineLiftedFunctions(const NativeModule *cfg_module);
} // namespace mcsema
-
-#endif // MCSEMA_BC_FUNCTION_H_
diff --git a/mcsema/BC/Instruction.cpp b/mcsema/BC/Instruction.cpp
index 92328edf1..0e4e4c625 100644
--- a/mcsema/BC/Instruction.cpp
+++ b/mcsema/BC/Instruction.cpp
@@ -1,19 +1,22 @@
/*
- * Copyright (c) 2017 Trail of Bits, Inc.
+ * Copyright (c) 2020 Trail of Bits, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
+#include "Instruction.h"
+
#include
#include
@@ -28,137 +31,69 @@
#include
#include
-#include "Instruction.h"
#include "remill/Arch/Arch.h"
#include "remill/Arch/Instruction.h"
#include "remill/BC/Util.h"
#include "mcsema/Arch/Arch.h"
-
#include "mcsema/BC/Callback.h"
-#include "mcsema/BC/Instruction.h"
#include "mcsema/BC/Lift.h"
#include "mcsema/BC/Util.h"
-
#include "mcsema/CFG/CFG.h"
+
namespace mcsema {
-namespace {
-
-// Load the address of a register.
-static llvm::Value *LoadRegAddress(llvm::BasicBlock *block,
- std::string reg_name) {
- return remill::FindVarInFunction(block->getParent(), reg_name);
-}
-
-// Load the value of a register.
-static llvm::Value *LoadRegValue(llvm::BasicBlock *block,
- std::string reg_name) {
- return new llvm::LoadInst(LoadRegAddress(block, reg_name), "", block);
-}
-
-static llvm::Value *LoadAddressRegVal(llvm::BasicBlock *block,
- const remill::Operand::Register ®,
- llvm::ConstantInt *zero) {
- if (reg.name.empty()) {
- return zero;
- }
-
- auto value = LoadRegValue(block, reg.name);
- auto value_type = llvm::dyn_cast(value->getType());
- auto word_type = zero->getType();
-
- CHECK(value_type)
- << "Register " << reg.name << " expected to be an integer.";
-
- auto value_size = value_type->getBitWidth();
- auto word_size = word_type->getBitWidth();
- CHECK(value_size <= word_size)
- << "Register " << reg.name << " expected to be no larger than the "
- << "machine word size (" << word_type->getBitWidth() << " bits).";
-
- if (value_size < word_size) {
- value = new llvm::ZExtInst(value, word_type, "", block);
- }
-
- return value;
-}
-
-static bool IsFramePointerReg(const remill::Operand::Register ®) {
-
- if (reg.name.empty()) {
- return false;
- }
-
- if (mcsema::gArch->IsAMD64()) {
- return reg.name == "RBP";
- } else if (mcsema::gArch->IsX86()) {
- return reg.name == "EBP";
- }
- return false;
-}
-
-static bool IsStackPointerReg(const remill::Operand::Register ®) {
-
- if (reg.name.empty()) {
- return false;
- }
-
- if (mcsema::gArch->IsAMD64()) {
- return reg.name == "RSP";
- } else if (mcsema::gArch->IsX86()) {
- return reg.name == "ESP";
- } else if (mcsema::gArch->IsAArch64()) {
- return reg.name == "SP" || reg.name == "WSP";
- }
- return false;
-}
-
-} // namespace
-
InstructionLifter::~InstructionLifter(void) {}
InstructionLifter::InstructionLifter(const remill::IntrinsicTable *intrinsics_,
TranslationContext &ctx_)
- : remill::InstructionLifter(gArch, intrinsics_),
+ : remill::InstructionLifter(gArch.get(), intrinsics_),
ctx(ctx_) {}
// Lift a single instruction into a basic block.
remill::LiftStatus InstructionLifter::LiftIntoBlock(
- remill::Instruction &inst, llvm::BasicBlock *block_) {
+ remill::Instruction &inst, llvm::BasicBlock *block_, bool is_delayed) {
inst_ptr = &inst;
block = block_;
- mem_ref = GetAddress(ctx.cfg_inst->mem);
- imm_ref = GetMaskedAddress(ctx.cfg_inst->imm);
- disp_ref = GetMaskedAddress(ctx.cfg_inst->disp);
+
+ if (ctx.cfg_inst) {
+ mem_ref = GetAddress(ctx.cfg_inst->mem);
+ imm_ref = GetAddress(ctx.cfg_inst->imm);
+ disp_ref = GetAddress(ctx.cfg_inst->disp);
+ } else {
+ mem_ref = nullptr;
+ imm_ref = nullptr;
+ disp_ref = nullptr;
+ }
mem_ref_used = false;
disp_ref_used = false;
imm_ref_used = false;
- auto status = this->remill::InstructionLifter::LiftIntoBlock(inst, block);
+ auto status = this->remill::InstructionLifter::LiftIntoBlock(
+ inst, block, is_delayed);
// If we have semantics for the instruction, then make sure that we were
// able to match cross-reference information to the instruction's operands.
if (remill::kLiftedInstruction == status) {
if (mem_ref && !mem_ref_used) {
- LOG(FATAL)
+ LOG(ERROR)
<< "Unused memory reference operand to " << std::hex
<< ctx.cfg_inst->mem->target_ea << " in instruction "
<< inst.Serialize() << std::dec;
}
if (imm_ref && !imm_ref_used) {
- LOG(FATAL)
+ LOG(ERROR)
<< "Unused immediate operand reference to " << std::hex
<< ctx.cfg_inst->imm->target_ea << " in instruction "
<< inst.Serialize() << std::dec;
}
if (disp_ref && !disp_ref_used) {
- LOG(FATAL)
+ LOG(ERROR)
<< "Unused displacement operand reference to " << std::hex
<< ctx.cfg_inst->disp->target_ea << " in instruction "
<< inst.Serialize() << std::dec;
@@ -168,149 +103,72 @@ remill::LiftStatus InstructionLifter::LiftIntoBlock(
return status;
}
-llvm::Value *InstructionLifter::GetMaskedAddress(const NativeXref *cfg_xref) {
- auto addr = GetAddress(cfg_xref);
- if (!addr || !cfg_xref->mask) {
- return addr;
- }
+//// Returns `true` if a given cross-reference is self-referential. That is,
+//// we'll have something like `jmp cs:EnterCriticalSection`, which references
+//// `EnterCriticalSection` in the `.idata` section of a PE file. But this
+//// location is our only "place" for the external `EnterCriticalSection`, so
+//// we point it back at itself.
+//static bool IsSelfReferential(const NativeXref *cfg_xref) {
+// if (!cfg_xref->target_segment) {
+// return false;
+// }
+//
+// auto it = cfg_xref->target_segment->entries.find(cfg_xref->target_ea);
+// if (it == cfg_xref->target_segment->entries.end()) {
+// return false;
+// }
+//
+// const auto &entry = it->second;
+// if (!entry.xref) {
+// return false;
+// }
+//
+// return entry.xref->target_ea == entry.ea;
+//}
- auto mask = llvm::ConstantInt::get(word_type, cfg_xref->mask);
- llvm::IRBuilder<> ir(block);
- return ir.CreateAnd(addr, mask);
-}
-
-// Returns `true` if a given cross-reference is self-referential. That is,
-// we'll have something like `jmp cs:EnterCriticalSection`, which references
-// `EnterCriticalSection` in the `.idata` section of a PE file. But this
-// location is our only "place" for the external `EnterCriticalSection`, so
-// we point it back at itself.
-static bool IsSelfReferential(const NativeXref *cfg_xref) {
- if (!cfg_xref->target_segment) {
- return false;
- }
-
- auto it = cfg_xref->target_segment->entries.find(cfg_xref->target_ea);
- if (it == cfg_xref->target_segment->entries.end()) {
- return false;
- }
-
- const auto &entry = it->second;
- if (!entry.xref) {
- return false;
- }
-
- return entry.xref->target_ea == entry.ea;
-}
-
-llvm::Value *InstructionLifter::GetAddress(const NativeXref *cfg_xref) {
+llvm::Value *InstructionLifter::GetAddress(
+ const NativeInstructionXref *cfg_xref) {
if (!cfg_xref) {
return nullptr;
}
- llvm::IRBuilder<> ir(block);
- if (auto cfg_func = cfg_xref->func) {
- llvm::Function *func = nullptr;
- llvm::Value *func_val = nullptr;
-
- // If this is a lifted function, then create a wrapper around it. The
- // idea is that this reference to a lifted function can be leaked to
- // native code as a callback, and so native code calling it must be able
- // to swap into the lifted context.
- if (cfg_func->is_external) {
- if (IsSelfReferential(cfg_xref)) {
- LOG(WARNING)
- << "Reference from " << std::hex << cfg_xref->ea
- << " to self-referential function reference "
- << cfg_xref->target_name << " at " << cfg_xref->target_ea
- << " being lifted as address into "
- << cfg_xref->target_segment->name << std::dec;
-
- return LiftEA(cfg_xref->target_segment, cfg_xref->target_ea);
-
- } else {
- func = gModule->getFunction(cfg_func->name);
- }
- } else {
- func = GetNativeToLiftedCallback(cfg_func);
- }
-
- CHECK(func != nullptr)
- << "Can't resolve reference to function "
- << cfg_func->name << " from " << std::hex << inst_ptr->pc << std::dec;
-
- func_val = func;
-
- // Functions attributed with weak linkage may be defined, but aren't
- // necessarily. What you end up getting is code that checks if the function
- // pointer is non-null, and if so, calls the function.
- //
- // mov rax, cs:__gmon_start___ptr
- // test rax, rax
- //
- // In the CFG, we get `__gmon_start__` instead of `__gmon_start___ptr`,
- // but we don't want to actually read the machine code bytes of
- // `__gmon_start__`, which is likely to be an ELF thunk. So we throw in
- // an extra layer of indirection here.
- //
-#if 0
- // TODO(akshayk): Discuss a better solution to handle the weak external functions
- // It causes the garbage address for _ZNSt12out_of_rangeD1Ev
-
- // TODO(pag): This is an awful hack for now that won't generalize.
- if (func->hasExternalWeakLinkage()) {
- LOG(ERROR)
- << "Adding pseudo-load of weak function " << cfg_func->name
- << " at " << std::hex << inst_ptr->pc << std::dec;
-
- auto temp_loc = ir.CreateAlloca(func->getType());
- ir.CreateStore(func, temp_loc);
- func_val = temp_loc;
- }
-#endif
-
- return ir.CreatePtrToInt(func_val, word_type);
-
- } else if (auto cfg_var = cfg_xref->var) {
- if (cfg_var->address) {
- return cfg_var->address;
- }
-
- LOG(ERROR)
- << "Variable " << cfg_var->name << " at " << std::hex << cfg_var->ea
- << std::dec << " was not lifted.";
- // Fall through.
+ if (cfg_xref->mask) {
+ return LiftXrefInCode(cfg_xref->target_ea & cfg_xref->mask);
+ } else {
+ return LiftXrefInCode(cfg_xref->target_ea);
}
-
- CHECK(cfg_xref->target_segment != nullptr)
- << "A non-function, non-variable cross-reference from "
- << std::hex << inst_ptr->pc << " to " << cfg_xref->target_ea << std::dec
- << " must be in a known segment.";
-
- return LiftEA(cfg_xref->target_segment, cfg_xref->target_ea);
}
llvm::Value *InstructionLifter::LiftImmediateOperand(
remill::Instruction &inst, llvm::BasicBlock *block,
llvm::Argument *arg, remill::Operand &op) {
auto arg_type = arg->getType();
- if (imm_ref) {
+ if (imm_ref && !imm_ref_used) {
imm_ref_used = true;
llvm::DataLayout data_layout(gModule.get());
auto arg_size = data_layout.getTypeSizeInBits(arg_type);
- CHECK(arg_size <= gArch->address_size)
+ CHECK(arg_size <= arch->address_size)
<< "Immediate operand size " << op.size << " of "
<< op.Serialize() << " in instuction " << std::hex << inst.pc
<< " is wider than the architecture pointer size ("
- << std::dec << gArch->address_size << ").";
+ << std::dec << arch->address_size << ").";
- if (arg_type != imm_ref->getType() && arg_size < gArch->address_size) {
+ if (arg_type != imm_ref->getType() && arg_size < arch->address_size) {
llvm::IRBuilder<> ir(block);
imm_ref = ir.CreateTrunc(imm_ref, arg_type);
}
return imm_ref;
+
+ } else if (op.size == arch->address_size &&
+ 4096 <= op.imm.val) {
+ auto seg = ctx.cfg_module->TryGetSegment(op.imm.val);
+ LOG_IF(WARNING, seg != nullptr)
+ << "Immediate operand '" << op.Serialize()
+ << "' of instruction " << inst.Serialize()
+ << " is a missed cross-reference candidate";
}
return this->remill::InstructionLifter::LiftImmediateOperand(
@@ -331,31 +189,6 @@ llvm::Value *InstructionLifter::LiftAddressOperand(
inst, block, arg, op);
}
- // Check if the instruction is referring to stack variable
- if (ctx.cfg_inst->stack_var) {
- llvm::IRBuilder<> ir(block);
- auto base = ir.CreatePtrToInt(ctx.cfg_inst->stack_var->llvm_var, word_type);
- auto map_it = ctx.cfg_inst->stack_var->refs.find(ctx.cfg_inst->ea);
- if (map_it != ctx.cfg_inst->stack_var->refs.end()) {
- auto var_offset = llvm::ConstantInt::get(
- word_type, static_cast(map_it->second), true);
- base = ir.CreateAdd(base, var_offset);
- LOG(INFO)
- << "Lifting stack variable access at : " << std::hex << map_it->first
- << " var_offset " << map_it->second << std::dec
- << " variable name " << ctx.cfg_inst->stack_var->name;
-
- if (!mem.index_reg.name.empty()) {
- auto zero = llvm::ConstantInt::get(word_type, 0, false);
- auto index = LoadAddressRegVal(block, mem.index_reg, zero);
- auto scale = llvm::ConstantInt::get(
- word_type, static_cast(mem.scale), true);
- return ir.CreateAdd(base, ir.CreateMul(index, scale));
- }
- }
- return base;
- }
-
if ((mem.base_reg.name.empty() && mem.index_reg.name.empty()) ||
(mem.base_reg.name == "PC" && mem.index_reg.name.empty())) {
@@ -368,6 +201,18 @@ llvm::Value *InstructionLifter::LiftAddressOperand(
return disp_ref;
}
+ } else if ((mem.base_reg.name.empty() && mem.index_reg.name.empty()) ||
+ (mem.base_reg.name == "NEXT_PC" && mem.index_reg.name.empty())) {
+
+ if (mem_ref) {
+ mem_ref_used = true;
+ return mem_ref;
+
+ } else if (disp_ref) {
+ disp_ref_used = true;
+ return disp_ref;
+ }
+
} else {
// It's a reference located in the displacement. We'll clear out the
@@ -381,8 +226,8 @@ llvm::Value *InstructionLifter::LiftAddressOperand(
llvm::IRBuilder<> ir(block);
return ir.CreateAdd(dynamic_addr, disp_ref);
- } else if (mem_ref && static_cast(op.addr.displacement) ==
- ctx.cfg_inst->mem->target_ea) {
+ } else if (mem_ref && (static_cast(op.addr.displacement) ==
+ ctx.cfg_inst->mem->target_ea)) {
LOG(ERROR)
<< "IDA probably incorrectly decoded memory operand "
<< op.Serialize() << " of instruction " << std::hex << inst.pc
@@ -401,36 +246,4 @@ llvm::Value *InstructionLifter::LiftAddressOperand(
inst, block, arg, op);
}
-// Lift the register operand to a value.
-llvm::Value *InstructionLifter::LiftRegisterOperand(
- remill::Instruction &inst, llvm::BasicBlock *block,
- llvm::Argument *arg, remill::Operand &op) {
-
- auto ® = op.reg;
-
- // Check if the instruction is referring to the base pointer which
- // might be accessing stack variable indirectly
- if (ctx.cfg_inst->stack_var) {
- if ((IsFramePointerReg(reg) || IsStackPointerReg(reg)) &&
- (op.action == remill::Operand::kActionRead)) {
- llvm::IRBuilder<> ir(block);
- auto variable = ir.CreatePtrToInt(
- ctx.cfg_inst->stack_var->llvm_var, word_type);
- auto map_it = ctx.cfg_inst->stack_var->refs.find(ctx.cfg_inst->ea);
- if (map_it != ctx.cfg_inst->stack_var->refs.end()) {
- auto var_offset = llvm::ConstantInt::get(
- word_type, static_cast(map_it->second), true);
- variable = ir.CreateAdd(variable, var_offset);
- }
- LOG(INFO)
- << "Lifting stack variable reference at : " << std::hex
- << ctx.cfg_inst->ea << std::dec;
- return variable;
- }
- }
-
- return this->remill::InstructionLifter::LiftRegisterOperand(
- inst, block, arg, op);
-}
-
} // namespace mcsema
diff --git a/mcsema/BC/Instruction.h b/mcsema/BC/Instruction.h
index 45dea9e65..83c533fdd 100644
--- a/mcsema/BC/Instruction.h
+++ b/mcsema/BC/Instruction.h
@@ -1,21 +1,21 @@
/*
- * Copyright (c) 2017 Trail of Bits, Inc.
+ * Copyright (c) 2020 Trail of Bits, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
-#ifndef MCSEMA_BC_INSTRUCTION_H_
-#define MCSEMA_BC_INSTRUCTION_H_
+#pragma once
#include
@@ -36,7 +36,7 @@ class IntrinsicTable;
namespace mcsema {
-struct NativeXref;
+struct NativeInstructionXref;
struct TranslationContext;
class InstructionLifter : public remill::InstructionLifter {
@@ -48,7 +48,8 @@ class InstructionLifter : public remill::InstructionLifter {
// Lift a single instruction into a basic block.
remill::LiftStatus LiftIntoBlock(remill::Instruction &inst,
- llvm::BasicBlock *block) override;
+ llvm::BasicBlock *block,
+ bool is_delayed) override;
protected:
@@ -62,15 +63,9 @@ class InstructionLifter : public remill::InstructionLifter {
remill::Instruction &inst, llvm::BasicBlock *block,
llvm::Argument *arg, remill::Operand &mem) override;
- // Lift a register operand to a value.
- llvm::Value *LiftRegisterOperand(
- remill::Instruction &inst, llvm::BasicBlock *block,
- llvm::Argument *arg, remill::Operand ®) override;
-
private:
- llvm::Value *GetAddress(const NativeXref *cfg_xref);
- llvm::Value *GetMaskedAddress(const NativeXref *cfg_xref);
+ llvm::Value *GetAddress(const NativeInstructionXref *cfg_xref);
TranslationContext &ctx;
@@ -87,5 +82,3 @@ class InstructionLifter : public remill::InstructionLifter {
};
} // namespace mcsema
-
-#endif // MCSEMA_BC_INSTRUCTION_H_
diff --git a/mcsema/BC/Legacy.cpp b/mcsema/BC/Legacy.cpp
index 9f6b66764..f4179b9cd 100644
--- a/mcsema/BC/Legacy.cpp
+++ b/mcsema/BC/Legacy.cpp
@@ -1,19 +1,22 @@
/*
- * Copyright (c) 2017 Trail of Bits, Inc.
+ * Copyright (c) 2020 Trail of Bits, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
+#include "Legacy.h"
+
#include
#include
@@ -45,24 +48,6 @@ namespace mcsema {
namespace legacy {
namespace {
-// Remove calls to error-related intrinsics.
-static void ImplementErrorIntrinsic(const char *name) {
- auto func = gModule->getFunction(name);
- if (!func) {
- return;
- }
-
- auto void_type = llvm::Type::getVoidTy(*gContext);
- auto abort_func = gModule->getOrInsertFunction(
- "abort", llvm::FunctionType::get(void_type, false));
-
- func->setLinkage(llvm::GlobalValue::InternalLinkage);
-
- llvm::IRBuilder<> ir(llvm::BasicBlock::Create(*gContext, "", func));
- ir.CreateCall(abort_func);
- ir.CreateRet(remill::NthArgument(func, remill::kMemoryPointerArgNum));
-}
-
// Create the node for a `mcsema_real_eip` annotation.
static llvm::MDNode *CreateInstAnnotation(llvm::Function *F, uint64_t addr) {
auto addr_val = llvm::ConstantInt::get(gWordType, addr);
@@ -146,10 +131,5 @@ void PropagateInstAnnotations(void) {
}
}
-void DowngradeModule(void) {
- ImplementErrorIntrinsic("__remill_error");
- ImplementErrorIntrinsic("__remill_missing_block");
-}
-
} // namespace legacy
} // namespace mcsema
diff --git a/mcsema/BC/Legacy.h b/mcsema/BC/Legacy.h
index 3f07b4c2e..4af0ff5e5 100644
--- a/mcsema/BC/Legacy.h
+++ b/mcsema/BC/Legacy.h
@@ -1,21 +1,21 @@
/*
- * Copyright (c) 2017 Trail of Bits, Inc.
+ * Copyright (c) 2020 Trail of Bits, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
-#ifndef MCSEMA_BC_LEGACY_H_
-#define MCSEMA_BC_LEGACY_H_
+#pragma once
#include
@@ -25,8 +25,6 @@ class Function;
namespace mcsema {
namespace legacy {
-void DowngradeModule(void);
-
// Create a `mcsema_real_eip` annotation, and annotate every unannotated
// instruction with this new annotation.
void AnnotateInsts(llvm::Function *func, uint64_t pc);
@@ -37,4 +35,3 @@ void PropagateInstAnnotations(void);
} // namespace legacy
} // namespace mcsema
-#endif // MCSEMA_BC_LEGACY_H_
diff --git a/mcsema/BC/Lift.cpp b/mcsema/BC/Lift.cpp
index f18231977..73ff26352 100644
--- a/mcsema/BC/Lift.cpp
+++ b/mcsema/BC/Lift.cpp
@@ -1,19 +1,22 @@
/*
- * Copyright (c) 2017 Trail of Bits, Inc.
+ * Copyright (c) 2020 Trail of Bits, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
+#include "Lift.h"
+
#include
#include
@@ -55,7 +58,6 @@
#include "mcsema/BC/External.h"
#include "mcsema/BC/Function.h"
#include "mcsema/BC/Legacy.h"
-#include "mcsema/BC/Lift.h"
#include "mcsema/BC/Optimize.h"
#include "mcsema/BC/Segment.h"
#include "mcsema/BC/Util.h"
@@ -70,22 +72,32 @@ namespace {
// Add entrypoint functions for any exported functions.
static void ExportFunctions(const NativeModule *cfg_module) {
- for (auto ea : cfg_module->exported_funcs) {
- auto cfg_func = cfg_module->ea_to_func.at(ea)->Get();
- CHECK(gModule->getFunction(cfg_func->lifted_name) != nullptr)
- << "Cannot find lifted version of exported function "
- << cfg_func->lifted_name;
+ for (auto [ea, cfg_func] : cfg_module->ea_to_func) {
+ (void) ea;
- LOG(INFO)
- << "Exporting function " << cfg_func->name;
+ if (cfg_func->function) {
+ cfg_func->function = gModule->getFunction(cfg_func->name);
+ }
- auto ep = GetNativeToLiftedEntryPoint(cfg_func);
- ep->setLinkage(llvm::GlobalValue::ExternalLinkage);
- ep->setVisibility(llvm::GlobalValue::DefaultVisibility);
-
- remill::TieFunctions(ep, gModule->getFunction(cfg_func->lifted_name));
- remill::Annotate(ep);
+ if (auto ep = cfg_func->function; ep) {
+ if (cfg_func->is_exported) {
+ LOG(INFO)
+ << "Exporting function " << cfg_func->name;
+ // remill::TieFunctions(ep, gModule->getFunction(cfg_func->lifted_name));
+ remill::Annotate(ep);
+ ep->setLinkage(llvm::GlobalValue::ExternalLinkage);
+ ep->setVisibility(llvm::GlobalValue::DefaultVisibility);
+ } else {
+ ep->setVisibility(llvm::GlobalValue::HiddenVisibility);
+ ep->setLinkage(llvm::GlobalValue::PrivateLinkage);
+ if (!ep->hasNUsesOrMore(1)) {
+ LOG(INFO)
+ << "Removing function " << cfg_func->name;
+ ep->eraseFromParent();
+ }
+ }
+ }
}
}
@@ -134,7 +146,8 @@ static void DefineDebugGetRegState(void) {
auto state_ptr_type = reg_state->getType();
auto reg_func_type = llvm::FunctionType::get(state_ptr_type, false);
- // ExternalWeakLinkage causes crash with --explicit_args. The function is not available in the library
+ // ExternalWeakLinkage causes crash with --explicit_args. The function is not
+ // available in the library
get_reg_state = llvm::Function::Create(
reg_func_type, llvm::GlobalValue::ExternalLinkage,
"__mcsema_debug_get_reg_state", gModule.get());
@@ -146,43 +159,81 @@ static void DefineDebugGetRegState(void) {
get_reg_state->addFnAttr(llvm::Attribute::OptimizeNone);
}
-// Define some of the remill error intrinsics.
-static void DefineErrorIntrinsics(llvm::FunctionType *lifted_func_type) {
- const char *func_names[] = {"__remill_error", "__remill_missing_block"};
- auto trap = llvm::Intrinsic::getDeclaration(gModule.get(), llvm::Intrinsic::trap);
- for (auto func_name : func_names) {
- auto func = gModule->getFunction(func_name);
- if (!func) {
- continue;
- }
- if (func->isDeclaration()) {
- llvm::IRBuilder<> ir(llvm::BasicBlock::Create(*gContext, "", func));
- ir.CreateCall(trap);
- ir.CreateUnreachable();
- }
+// Remove calls to error-related intrinsics.
+static void ImplementErrorIntrinsic(const char *name) {
+ auto func = gModule->getFunction(name);
+ if (!func || !func->isDeclaration()) {
+ return;
}
+
+ auto void_type = llvm::Type::getVoidTy(*gContext);
+ auto abort_func = gModule->getFunction("abort");
+ if (!abort_func) {
+ abort_func = llvm::Function::Create(
+ llvm::FunctionType::get(void_type, false),
+ llvm::GlobalValue::ExternalLinkage,
+ "abort",
+ gModule.get());
+
+ abort_func->addFnAttr(llvm::Attribute::NoReturn);
+
+ // Might be an externally-defined function :-/
+ } else if (abort_func->getFunctionType()->getNumParams() ||
+ !abort_func->getFunctionType()->getReturnType()->isVoidTy()) {
+ abort_func = llvm::Intrinsic::getDeclaration(
+ gModule.get(), llvm::Intrinsic::trap);
+ }
+
+ func->setLinkage(llvm::GlobalValue::InternalLinkage);
+ func->removeFnAttr(llvm::Attribute::NoInline);
+ func->removeFnAttr(llvm::Attribute::OptimizeNone);
+ func->addFnAttr(llvm::Attribute::NoReturn);
+ func->addFnAttr(llvm::Attribute::InlineHint);
+ func->addFnAttr(llvm::Attribute::AlwaysInline);
+
+ llvm::IRBuilder<> ir(llvm::BasicBlock::Create(*gContext, "", func));
+
+ ir.CreateCall(abort_func);
+ ir.CreateUnreachable();
+}
+
+// Define some of the remill error intrinsics.
+static void DefineErrorIntrinsics(void) {
+ ImplementErrorIntrinsic("__remill_error");
+ ImplementErrorIntrinsic("__remill_missing_block");
}
} // namespace
-bool LiftCodeIntoModule(const NativeModule *cfg_module) {
+TranslationContext::TranslationContext(void) {}
- DeclareExternals(cfg_module);
+TranslationContext::~TranslationContext(void) {}
+
+bool LiftCodeIntoModule(const NativeModule *cfg_module) {
DeclareLiftedFunctions(cfg_module);
- DeclareDataSegments(cfg_module);
+ // Lift the blocks of instructions into the declared functions.
+ if (!DefineLiftedFunctions(cfg_module)) {
+ return false;
+ }
+
+ DefineErrorIntrinsics();
+
+ // Optimize the lifted bitcode.
+ OptimizeModule(cfg_module);
// Segments are only filled in after the lifted function declarations,
// external vars, and segments are declared so that cross-references to
// lifted things can be resolved.
DefineDataSegments(cfg_module);
- auto lifted_func_type = remill::LiftedFunctionType(gModule.get());
+ // Generate code to call pre-`main` function static object constructors, and
+ // post-`main` functions destructors.
+ CallInitFiniCode(cfg_module);
- // Lift the blocks of instructions into the declared functions.
- if (!DefineLiftedFunctions(cfg_module)) {
- return false;
- }
+ // Remove leftover Remill intrinsics, and lower memory access intrincis into
+ // `load` and `store` instructions.
+ CleanUpModule(cfg_module);
// Add entrypoint functions for any exported functions.
ExportFunctions(cfg_module);
@@ -190,20 +241,9 @@ bool LiftCodeIntoModule(const NativeModule *cfg_module) {
// Export any variables that should be externally visible.
ExportVariables(cfg_module);
- // Generate code to call pre-`main` function static object constructors, and
- // post-`main` functions destructors.
- CallInitFiniCode(cfg_module);
-
- if (FLAGS_legacy_mode) {
- legacy::DowngradeModule();
- }
-
- OptimizeModule();
-
if (FLAGS_explicit_args) {
DefineGCCStackGuard();
DefineDebugGetRegState();
- DefineErrorIntrinsics(lifted_func_type);
}
if (!FLAGS_pc_annotation.empty()) {
diff --git a/mcsema/BC/Lift.h b/mcsema/BC/Lift.h
index e8b087c14..48381f894 100644
--- a/mcsema/BC/Lift.h
+++ b/mcsema/BC/Lift.h
@@ -1,31 +1,38 @@
/*
- * Copyright (c) 2017 Trail of Bits, Inc.
+ * Copyright (c) 2020 Trail of Bits, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
-#ifndef MCSEMA_BC_LIFT_H_
-#define MCSEMA_BC_LIFT_H_
+#pragma once
+#include
+#include
+#include
#include
+#include "remill/Arch/Instruction.h"
+
namespace remill {
class InstructionLifter;
} // namespace remill
namespace llvm {
+class AllocaInst;
class BasicBlock;
class Function;
+class Value;
} // namespace llvm
namespace mcsema {
@@ -34,20 +41,32 @@ struct NativeModule;
struct NativeFunction;
struct NativeBlock;
struct NativeInstruction;
+struct NativePreservedRegisters;
struct TranslationContext {
- remill::InstructionLifter *lifter;
- const NativeModule *cfg_module;
- const NativeFunction *cfg_func;
- const NativeBlock *cfg_block;
- const NativeInstruction *cfg_inst;
- llvm::Function *lifted_func;
+ TranslationContext(void);
+ ~TranslationContext(void);
+
+ remill::InstructionLifter *lifter = nullptr;
+ const NativeModule *cfg_module = nullptr;
+ const NativeFunction *cfg_func = nullptr;
+ const NativeBlock *cfg_block = nullptr;
+ const NativeInstruction *cfg_inst = nullptr;
+ llvm::Function *lifted_func = nullptr;
std::unordered_map ea_to_block;
std::unordered_map lp_to_block;
+ std::vector> preserved_regs;
+ std::unordered_map
+ func_preserved_regs;
+ remill::Instruction inst;
+ remill::Instruction delayed_inst;
+ std::vector> work_list;
+
+ llvm::BasicBlock *entry_block{nullptr};
+ llvm::Value *stack_ptr_var{nullptr};
+ llvm::Value *frame_ptr_var{nullptr};
};
bool LiftCodeIntoModule(const NativeModule *cfg_module);
} // namespace mcsema
-
-#endif // MCSEMA_BC_LIFT_H_
diff --git a/mcsema/BC/Optimize.cpp b/mcsema/BC/Optimize.cpp
index e70c8562a..e94f997aa 100644
--- a/mcsema/BC/Optimize.cpp
+++ b/mcsema/BC/Optimize.cpp
@@ -1,19 +1,22 @@
/*
- * Copyright (c) 2017 Trail of Bits, Inc.
+ * Copyright (c) 2020 Trail of Bits, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
*/
+#include "mcsema/BC/Optimize.h"
+
#include
#include
@@ -45,22 +48,41 @@
#include
#include
-#include "remill/Arch/Arch.h"
-#include "remill/BC/ABI.h"
-#include "remill/BC/Compat/TargetLibraryInfo.h"
-#include "remill/BC/DeadStoreEliminator.h"
-#include "remill/BC/Util.h"
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
#include "mcsema/Arch/Arch.h"
#include "mcsema/BC/Optimize.h"
#include "mcsema/BC/Util.h"
-
-DEFINE_bool(disable_optimizer, false,
- "Disable interprocedural optimizations?");
+#include "mcsema/CFG/CFG.h"
DEFINE_bool(keep_memops, false,
"Should the memory intrinsics be replaced or not?");
+DEFINE_bool(check_for_lowmem_xrefs, false,
+ "Check every constant, even those less than 4096, to see "
+ "if they might be cross-reference targets. This might be "
+ "reasonable to enable for PIC code, .o files, etc.");
+
+DEFINE_bool(volatile_memops, false, "Mark all lowered loads/stores as volatile");
+
+DEFINE_bool(local_state_pointer, true,
+ "Use the state pointer passed by argument to all lifted functions."
+ "Set local_state_pointer to false to disable it.");
+
+DEFINE_bool(restore_all_on_unreachable, false,
+ "Ensure that functions containing unreachable code end up "
+ "restoring all saved registers on returning paths.");
+
+DECLARE_bool(disable_aliases);
+
namespace mcsema {
namespace {
@@ -81,6 +103,12 @@ static void ReplaceUndefIntrinsic(llvm::Function *function) {
static void RemoveFunction(llvm::Function *func) {
if (!func->hasNUsesOrMore(1)) {
func->eraseFromParent();
+ } else {
+ auto ret_type = func->getReturnType();
+ if (!ret_type->isVoidTy()) {
+ func->replaceAllUsesWith(llvm::UndefValue::get(func->getType()));
+ func->eraseFromParent();
+ }
}
}
@@ -109,41 +137,6 @@ static void RemoveUndefFuncCalls(void) {
}
}
-static void RunO3(void) {
- llvm::legacy::FunctionPassManager func_manager(gModule.get());
- llvm::legacy::PassManager module_manager;
-
- auto TLI = new llvm::TargetLibraryInfoImpl(
- llvm::Triple(gModule->getTargetTriple()));
-
- TLI->disableAllFunctions(); // `-fno-builtin`.
-
- llvm::PassManagerBuilder builder;
- builder.OptLevel = 3;
- builder.SizeLevel = 2;
- builder.Inliner = llvm::createFunctionInliningPass(
- std::numeric_limits::max());
- builder.LibraryInfo = TLI; // Deleted by `llvm::~PassManagerBuilder`.
- builder.DisableUnrollLoops = false; // Unroll loops!
- IF_LLVM_LT_900(builder.DisableUnitAtATime = false;)
- builder.SLPVectorize = false;
- builder.LoopVectorize = false;
- IF_LLVM_GTE_360(builder.VerifyInput = false;)
- IF_LLVM_GTE_360(builder.VerifyOutput = false;)
-
- // TODO(pag): Not sure when this became available.
- IF_LLVM_GTE_800(builder.MergeFunctions = false;)
-
- builder.populateFunctionPassManager(func_manager);
- builder.populateModulePassManager(module_manager);
- func_manager.doInitialization();
- for (auto &func : *gModule) {
- func_manager.run(func);
- }
- func_manager.doFinalization();
- module_manager.run(*gModule);
-}
-
// Get a list of all ISELs.
static std::vector FindISELs(void) {
std::vector isels;
@@ -167,50 +160,6 @@ static void PrivatizeISELs(std::vector &isels) {
}
}
-// Remove some of the remill intrinsics.
-static void RemoveIntrinsics(void) {
- if (auto llvm_used = gModule->getGlobalVariable("llvm.used")) {
- llvm_used->eraseFromParent();
- }
-
- // This function makes removing intrinsics tricky, so if it's there, then
- // we'll try to get the optimizer to inline it on our behalf, which should
- // drop some references :-D
- if (auto remill_used = gModule->getFunction("__remill_mark_as_used")) {
- std::vector uses;
- for (auto use : remill_used->users()) {
- if (auto call = llvm::dyn_cast(use)) {
- uses.push_back(call);
- }
- }
-
- for (auto call : uses) {
- call->eraseFromParent();
- }
-
- if (remill_used->hasNUsesOrMore(1)) {
- if (remill_used->isDeclaration()) {
- remill_used->setLinkage(llvm::GlobalValue::InternalLinkage);
- remill_used->removeFnAttr(llvm::Attribute::NoInline);
- remill_used->addFnAttr(llvm::Attribute::InlineHint);
- remill_used->addFnAttr(llvm::Attribute::AlwaysInline);
- auto block = llvm::BasicBlock::Create(*gContext, "", remill_used);
- (void) llvm::ReturnInst::Create(*gContext, block);
- }
- }
-
- RemoveFunction(remill_used);
- }
-
-// if (auto intrinsics = gModule->getFunction("__remill_intrinsics")) {
-// intrinsics->eraseFromParent();
-// }
-
- RemoveFunction("__remill_basic_block");
- RemoveFunction("__remill_defer_inlining");
- RemoveFunction("__remill_intrinsics");
-}
-
static void ReplaceBarrier(const char *name) {
auto func = gModule->getFunction(name);
if (!func) {
@@ -228,8 +177,449 @@ static void ReplaceBarrier(const char *name) {
}
}
+static llvm::Value *FindPointer(
+ llvm::IRBuilder<> &ir, llvm::Value *addr, llvm::Type *elem_type,
+ unsigned addr_space) {
+
+ if (auto as_ptr_to_int = llvm::dyn_cast(addr)) {
+ if (!addr_space) {
+ addr_space = as_ptr_to_int->getPointerAddressSpace();
+ }
+ auto curr = as_ptr_to_int->getPointerOperand();
+ auto possible = FindPointer(ir, curr, elem_type, addr_space);
+ return possible ? possible : curr;
+
+ } else {
+ return nullptr;
+ }
+}
+
+unsigned GetPointerAddressSpace(llvm::Value *val, unsigned addr_space) {
+ if (addr_space || !val) {
+ return addr_space;
+ }
+
+ if (auto source_type = llvm::dyn_cast(val->getType())) {
+ addr_space = source_type->getPointerAddressSpace();
+ if (addr_space) {
+ return addr_space;
+ }
+ }
+
+ if (auto as_bc = llvm::dyn_cast(val)) {
+ return GetPointerAddressSpace(as_bc->getOperand(0), addr_space);
+
+ } else if (auto as_pti = llvm::dyn_cast(val)) {
+ return GetPointerAddressSpace(as_pti->getOperand(0), addr_space);
+
+ } else if (auto as_itp = llvm::dyn_cast(val)) {
+ return GetPointerAddressSpace(as_itp->getOperand(0), addr_space);
+
+ } else if (auto as_addr = llvm::dyn_cast(val)) {
+ return GetPointerAddressSpace(as_addr->getOperand(0), addr_space);
+
+ } else {
+ return addr_space;
+ }
+}
+
+// Try to get an Value representing the address of `ea` as an entity, or return
+// `nullptr`.
+static llvm::Constant *GetAddress(const NativeModule *cfg_module, uint64_t ea) {
+ if (auto cfg_var = cfg_module->TryGetVariable(ea); cfg_var) {
+ return cfg_var->Address();
+
+ } else if (auto cfg_func = cfg_module->TryGetFunction(ea); cfg_func) {
+ return cfg_func->Address();
+
+ } else if (auto cfg_seg = cfg_module->TryGetSegment(ea); cfg_seg) {
+ return LiftXrefInData(cfg_seg, ea);
+
+ // Look to see if `ea - 1` is the last byte of an existing segment or
+ // variable, and if so, create a GEP that gets the address immediately
+ // following the segment.
+ //
+ // NOTE(pag): In practice, we don't need to worry about segment padding
+ // here, as we expect that this pointer is used as an upper bound
+ // for some computation, and the referenced address is not
+ // actually mapped, so it's OK that it points into "the next
+ // element's" padding.
+ } else {
+ if (auto cfg_var = cfg_module->TryGetVariable(ea - 1); cfg_var) {
+ auto i32_type = llvm::Type::getInt32Ty(*gContext);
+ auto var = cfg_var->Pointer();
+ auto var_type = var->getType()->getPointerElementType();
+ auto ptr = llvm::ConstantExpr::getGetElementPtr(
+ var_type, var, llvm::ConstantInt::get(i32_type, 1, false));
+ return llvm::ConstantExpr::getPtrToInt(ptr, gWordType);
+
+ } else if (auto cfg_seg = cfg_module->TryGetSegment(ea - 1); cfg_seg) {
+ auto i32_type = llvm::Type::getInt32Ty(*gContext);
+ auto seg_var = cfg_seg->Pointer();
+ auto seg_type = seg_var->getType()->getPointerElementType();
+ const auto ptr = llvm::ConstantExpr::getGetElementPtr(
+ seg_type, seg_var, llvm::ConstantInt::get(i32_type, 1, false));
+ return llvm::ConstantExpr::getPtrToInt(ptr, gWordType);
+ }
+ }
+
+ return nullptr;
+}
+
+static llvm::Value *GetPointer(
+ const NativeModule *cfg_module, llvm::IRBuilder<> &ir,
+ llvm::Value *addr, llvm::Type *elem_type, unsigned addr_space);
+
+static llvm::Value *GetIndexedPointer(
+ const NativeModule *cfg_module, llvm::IRBuilder<> &ir,
+ llvm::Value *lhs, llvm::Value *rhs, llvm::Type *dest_type,
+ unsigned addr_space) {
+
+ auto i32_ty = llvm::Type::getInt32Ty(*gContext);
+ auto i8_ty = llvm::Type::getInt8Ty(*gContext);
+ auto i8_ptr_ty = llvm::PointerType::get(i8_ty, addr_space);
+
+ if (auto rhs_const = llvm::dyn_cast(rhs)) {
+ const auto rhs_index = static_cast(rhs_const->getSExtValue());
+
+ const auto &dl = gModule->getDataLayout();
+
+ const auto [new_lhs, index] =
+ remill::StripAndAccumulateConstantOffsets(dl, lhs);
+
+ // It's possible that we will index into, but beyond, one global variable,
+ // intending to get to another global.
+ //
+ // NOTE(pag): We use `getTypeStoreSize` of `global_type` so that we deal
+ // with the defined bytes, and not any implied alignment of
+ // globals lifted due to things like section alignment.
+ if (auto lhs_global = llvm::dyn_cast(new_lhs)) {
+
+ if (cfg_module) {
+ if (auto cfg_seg = cfg_module->TryGetSegment(lhs_global->getName())) {
+ const auto real_ea = static_cast(
+ static_cast(cfg_seg->ea) + index);
+
+ if (auto addr = GetAddress(cfg_module, real_ea)) {
+ LOG_IF(WARNING, cfg_module->TryGetSegment(real_ea) != cfg_seg)
+ << "Fixing cross-reference to " << std::hex
+ << real_ea << std::dec << " that was misplaced into segment "
+ << cfg_seg->name;
+
+ return GetPointer(
+ nullptr, ir, addr, dest_type->getPointerElementType(),
+ addr_space);
+
+ } else {
+ LOG(ERROR)
+ << "Out-of-bounds reference to " << std::hex << real_ea
+ << std::dec << " tied to segment " << cfg_seg->name;
+ }
+ }
+ }
+
+ if (!index) {
+ return ir.CreateBitCast(lhs_global, dest_type);
+ }
+
+ // It's a global variable not associated with a native segment, try to
+ // index into it in a natural-ish way. We only apply this when the index
+ // is positive.
+ if (0 < index) {
+ auto offset = static_cast(index);
+ return remill::BuildPointerToOffset(ir, lhs_global, offset, dest_type);
+ }
+ }
+
+ auto lhs_elem_type = lhs->getType()->getPointerElementType();
+ auto dest_elem_type = dest_type->getPointerElementType();
+
+ const auto lhs_el_size = dl.getTypeAllocSize(lhs_elem_type);
+ const auto dest_el_size = dl.getTypeAllocSize(dest_elem_type);
+
+ llvm::Value *ptr = nullptr;
+
+ // If either the source or destination element size is divisible by the
+ // other then we might get lucky and be able to compute a pointer to the
+ // destination with a single GEP.
+ if (!(lhs_el_size % dest_el_size) || !(dest_el_size % lhs_el_size)) {
+
+ if (0 > rhs_index) {
+ const auto pos_rhs_index = static_cast(-rhs_index);
+ if (!(pos_rhs_index % lhs_el_size)) {
+ const auto scaled_index = static_cast(
+ rhs_index / static_cast(lhs_el_size));
+ llvm::Value *indices[1] = {
+ llvm::ConstantInt::get(i32_ty, scaled_index, true)};
+ ptr = ir.CreateGEP(lhs_elem_type, lhs, indices);
+ }
+ } else {
+ const auto pos_rhs_index = static_cast(rhs_index);
+ if (!(pos_rhs_index % lhs_el_size)) {
+ const auto scaled_index = static_cast(
+ rhs_index / static_cast(lhs_el_size));
+ llvm::Value *indices[1] = {
+ llvm::ConstantInt::get(i32_ty, scaled_index, false)};
+ ptr = ir.CreateGEP(lhs_elem_type, lhs, indices);
+ }
+ }
+ }
+
+ // We got a GEP for the dest, now make sure it's the right type.
+ if (ptr) {
+ if (lhs->getType() == dest_type) {
+ return ptr;
+ } else {
+ return ir.CreateBitCast(ptr, dest_type);
+ }
+ }
+ }
+
+ auto base = ir.CreateBitCast(lhs, i8_ptr_ty);
+ llvm::Value *indices[1] = {ir.CreateTrunc(rhs, i32_ty)};
+ auto gep = ir.CreateGEP(i8_ty, base, indices);
+ return ir.CreateBitCast(gep, dest_type);
+}
+
+// Try to get a pointer for the address operand of a remill memory access
+// intrinsic.
+static llvm::Value *GetPointerFromInt(
+ llvm::IRBuilder<> &ir, llvm::Value *addr, llvm::Type *elem_type,
+ unsigned addr_space) {
+ auto dest_type = llvm::PointerType::get(elem_type, addr_space);
+
+ if (auto phi = llvm::dyn_cast(addr)) {
+ const auto old_ipoint = &*(ir.GetInsertPoint());
+ ir.SetInsertPoint(phi);
+
+ const auto max = phi->getNumIncomingValues();
+ const auto new_phi = ir.CreatePHI(dest_type, max);
+
+ for (auto i = 0u; i < max; ++i) {
+ auto val = phi->getIncomingValue(i);
+ auto block = phi->getIncomingBlock(i);
+ llvm::IRBuilder<> sub_ir(block->getTerminator());
+ auto ptr = FindPointer(sub_ir, val, elem_type, addr_space);
+ if (ptr) {
+ if (ptr->getType() != dest_type) {
+ ptr = sub_ir.CreateBitCast(ptr, dest_type);
+ }
+ } else {
+ ptr = sub_ir.CreateIntToPtr(val, dest_type);
+ }
+ new_phi->addIncoming(ptr, block);
+ }
+
+ ir.SetInsertPoint(old_ipoint);
+
+ return new_phi;
+
+ } else {
+ return ir.CreateIntToPtr(addr, dest_type);
+ }
+}
+
+// Try to get a pointer for the address operand of a remill memory access
+// intrinsic.
+llvm::Value *GetPointer(
+ const NativeModule *cfg_module, llvm::IRBuilder<> &ir,
+ llvm::Value *addr, llvm::Type *elem_type, unsigned addr_space) {
+
+ addr_space = GetPointerAddressSpace(addr, addr_space);
+ const auto addr_type = addr->getType();
+ auto dest_type = llvm::PointerType::get(elem_type, addr_space);
+
+ // Handle this case first so that we don't return early on the `ptrtoint` that
+ // may directly reach into the address parameter of the memory access
+ // intrinsics.
+ if (auto as_itp = llvm::dyn_cast(addr); as_itp) {
+ llvm::IRBuilder<> sub_ir(as_itp);
+ return GetPointer(
+ cfg_module, sub_ir, as_itp->getOperand(0), elem_type, addr_space);
+
+ // It's a `ptrtoint`, but of the wrong type; lets go back and try to use
+ // that pointer.
+ } else if (auto as_pti = llvm::dyn_cast(addr); as_pti) {
+ return GetPointer(
+ cfg_module, ir, as_pti->getPointerOperand(), elem_type, addr_space);
+
+ // We've found a pointer of the desired type; return :-D
+ } else if (addr_type == dest_type) {
+ return addr;
+
+ // A missed cross-reference!
+ } else if (auto ci = llvm::dyn_cast(addr); ci) {
+ const auto ea = ci->getZExtValue();
+ if (auto addr = GetAddress(cfg_module, ea); addr) {
+ return GetPointer(cfg_module, ir, addr, elem_type, addr_space);
+
+ } else {
+ LOG(ERROR)
+ << "Missed cross-reference target " << std::hex
+ << ea << " to pointer";
+ return llvm::ConstantExpr::getIntToPtr(ci, dest_type);
+ }
+
+ // It's a constant expression, the one we're interested in is `inttoptr`
+ // as we've already handled `ptrtoint` above.
+ } else if (auto ce = llvm::dyn_cast(addr); ce) {
+ if (ce->getOpcode() == llvm::Instruction::IntToPtr) {
+ return GetPointer(
+ cfg_module, ir, ce->getOperand(0), elem_type, addr_space);
+
+ } else if (addr_type->isIntegerTy()) {
+ return llvm::ConstantExpr::getIntToPtr(ce, dest_type);
+
+ } else {
+ CHECK(addr_type->isPointerTy());
+ return llvm::ConstantExpr::getBitCast(ce, dest_type);
+ }
+
+ } else if (llvm::isa(addr)) {
+ return ir.CreateBitCast(addr, dest_type);
+
+ } else if (auto as_add = llvm::dyn_cast(addr); as_add) {
+ const auto lhs_op = as_add->getOperand(0);
+ const auto rhs_op = as_add->getOperand(1);
+ auto lhs = FindPointer(ir, lhs_op, elem_type, addr_space);
+ auto rhs = FindPointer(ir, rhs_op, elem_type, addr_space);
+
+ if (!lhs && !rhs) {
+
+ auto lhs_inst = llvm::dyn_cast(lhs_op);
+ auto lhs_const = llvm::dyn_cast(lhs_op);
+
+ auto rhs_inst = llvm::dyn_cast(rhs_op);
+ auto rhs_const = llvm::dyn_cast(rhs_op);
+
+ // If we see something like the following:
+ //
+ // %res = add %lhs_inst,
+ // %ptr = inttoptr %res
+ //
+ // Then go find/create a pointer for `%lhs_inst`, then generate a GEP
+ // based off of that. This is to address a common pattern that we observe
+ // with things like accesses through the stack pointer.
+ if (lhs_inst && rhs_const && lhs_inst->hasNUsesOrMore(2)) {
+ auto ipoint = lhs_inst->getNextNode();
+ while (llvm::isa(ipoint)) {
+ ipoint = ipoint->getNextNode();
+ }
+ llvm::IRBuilder<> sub_ir(ipoint);
+ lhs = GetPointer(cfg_module, sub_ir, lhs_inst, elem_type, addr_space);
+
+ } else if (lhs_const && rhs_inst && rhs_inst->hasNUsesOrMore(2)) {
+ auto ipoint = rhs_inst->getNextNode();
+ while (llvm::isa