├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── LICENSE ├── Makefile ├── Makefile.macros ├── Makefile.rules ├── README.md ├── align_command.cc ├── align_command.h ├── auto_cleanup.h ├── command.cc ├── command.h ├── create-package ├── diff_command.cc ├── diff_command.h ├── docs ├── code-of-conduct.md └── contributing.md ├── elf_bin.cc ├── elf_bin.h ├── elf_error.cc ├── elf_error.h ├── elf_rela.cc ├── elf_rela.h ├── elf_symbol.cc ├── elf_symbol.h ├── examples └── llpatch-callback-shadow-var.c ├── fixup_command.cc ├── fixup_command.h ├── gen-symbol-map ├── gen_command.cc ├── gen_command.h ├── libutil.bash ├── livepatch-cc ├── livepatch-compile ├── llpatch ├── llpatch-merge ├── main.cc ├── symbol_map.cc ├── symbol_map.h ├── templates ├── Makefile.tmpl ├── README ├── livepatch.c.tmpl ├── livepatch.lds.tmpl ├── llpatch-callbacks.c └── llpatch.h ├── thin_archive.cc ├── thin_archive.h ├── third_party └── llvm-diff │ ├── DiffConsumer.cpp │ ├── DiffConsumer.h │ ├── DiffLog.cpp │ ├── DiffLog.h │ ├── DifferenceEngine.cpp │ ├── DifferenceEngine.h │ ├── LICENSE │ ├── Makefile │ └── README.md └── update-patch /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Expected Behavior 2 | 3 | 4 | ## Actual Behavior 5 | 6 | 7 | ## Steps to Reproduce the Problem 8 | 9 | 1. 10 | 1. 11 | 1. 12 | 13 | ## Specifications 14 | 15 | - Version: 16 | - Platform: -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes # 2 | 3 | > It's a good idea to open an issue first for discussion. 4 | 5 | - [ ] Tests pass 6 | - [ ] Appropriate changes to README are included in PR -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | livepatch 2 | GPATH 3 | GRTAGS 4 | GTAGS 5 | *.diff 6 | *.patch 7 | *.a 8 | .build 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ------------------------------------------------------------------------- 15 | # Makefile: makefile template 16 | # 17 | # Author: yonghyun@google.com (Yonghyun Hwang) 18 | # ------------------------------------------------------------------------- 19 | 20 | # 0: list of source codes 21 | # - if specified, only those are compiled 22 | # - if not specified, all *.c, *.cpp, and *.cc will be compiled 23 | SRCS = 24 | 25 | # 1: list of sub directories 26 | # - if specified, make recursively for the sub dirs 27 | SUB_DIRS = third_party/llvm-diff 28 | 29 | # 2: list of compile options (e.g., -Ddefine, -Iinc, ...) 30 | LOCAL_CXXFLAGS = -I/usr/lib/llvm-11/include -std=c++17 31 | LOCAL_CFLAGS = 32 | 33 | # 3: list of link options (e.g., -lm, -Labc, ...) 34 | LOCAL_LIB = -L/usr/lib/llvm-11/lib -Lthird_party/llvm-diff -lllvm-diff -lLLVM -lelf 35 | 36 | # 4: name for a.out or library 37 | # - specify LIB_NAME if you want to create libLIB_NAME.so out of your SRCS 38 | # - specify EXE if you want to create executable binary w/ the name, EXE 39 | # - DO __NOT__ specify both of them. if nothing specified, EXE = project 40 | LIB_NAME = 41 | EXE = livepatch 42 | 43 | # 5: specify compiler 44 | # - if not specified, CC = gcc, CXX= g++ 45 | CC = 46 | CXX = clang++ 47 | 48 | # 6: specify path to dir w/ Makefile.rules and Makefile.macros in it 49 | PRJ_ROOT_DIR ?= . 50 | 51 | # -------------------------------------------- 52 | # Magic part: please don't touch if you don't know what you are doing 53 | # -------------------------------------------- 54 | include $(PRJ_ROOT_DIR)/Makefile.rules 55 | $(eval $(call play_magic,$(EXE))) 56 | -------------------------------------------------------------------------------- /Makefile.macros: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | #--------------------------------------------- 15 | # Makefile.macros: common macros 16 | # 17 | # Author: yonghyun@google.com (Yonghyun Hwang) 18 | # ------------------------------------------------------------------------- 19 | 20 | # Project information 21 | 22 | PACKAGE := project 23 | VERSION_MAJOR := 0 24 | VERSION_MINOR := 0 25 | VERSION_PATCH := 0 26 | VERSION := $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH) 27 | RELEASE := 28 | PROJECT_VERSION := $(PACKAGE)-$(VERSION)-$(RELEASE) 29 | PROJECT := $(PROJECT_VERSION:-=) 30 | 31 | # -------------------------------------------- 32 | # Magic part: please don't touch if you don't know what you are doing 33 | # -------------------------------------------- 34 | 35 | # Installation directories: variable names adhere to GNU Makefile conventions 36 | 37 | prefix := $(PACKAGE) 38 | exec_prefix := $(prefix) 39 | bindir := $(exec_prefix)/bin 40 | libdir := $(exec_prefix)/lib 41 | sysconfdir := $(prefix)/etc 42 | includedir := $(prefix)/inc 43 | mandir := $(prefix)/man 44 | docdir := $(prefix)/docs 45 | 46 | # System-dependent global compile & link options 47 | 48 | SYS_DEFS := 49 | SYS_HEADER_INCS := 50 | SYS_LIB_INCS := 51 | SYS_LIBS := 52 | 53 | # Default macros for compiling and linking 54 | 55 | ifeq ($(CC),) 56 | CC := gcc # C compiler 57 | endif 58 | ifeq ($(CXX),) 59 | CXX := g++ # C++ compiler 60 | endif 61 | CPP := ${CC} -E # C preprocessor 62 | CXXCPP := ${CXX} -E # C++ preprocessor 63 | LD := ld # C Linker 64 | LDXX := ld # C++ Linker 65 | CPPFLAGS := -MMD # Preprocessor flags 66 | CFLAGS := -Wall -pipe # C compiler flags 67 | CXXFLAGS := -Wall -pipe # C++ compiler flags 68 | LDFLAGS := # C Linker flags 69 | LDXXFLAGS := # C++ Linker flags 70 | 71 | ifeq ($(EXE),) 72 | EXE := $(PACKAGE) 73 | endif 74 | 75 | # File permissions 76 | # Below are future use. When porting the project into Unix machine, they can be used 77 | 78 | FILE_GROUP := project # 'project' is temporary name for grp 79 | FILE_DPERM := 0640 # permission for data 80 | FILE_EPERM := 0750 # permission for executable bins or libs 81 | FILE_PERM := u+rwX,g-w+rX,o-rwx 82 | INSTALL_GROUP := project # 'project' is temporary 83 | INSTALL_DPERM := 0640 84 | INSTALL_EPERM := 0750 85 | INSTALL_PERM := u+rwX,g-sw+rX,o-rwx 86 | 87 | 88 | # Installation commands 89 | 90 | UMASK := umask 91 | CHMOD := chmod 92 | CHMOD_PROGRAM := $(CHMOD) $(INSTALL_EPERM) 93 | CHMOD_DATA := $(CHMOD) $(INSTALL_DPERM) 94 | CHGRP := chgrp 95 | CHGRP_PROGRAM := $(CHGRP) $(INSTALL_GROUP) 96 | CHGRP_DATA := $(CHGRP) $(INSTALL_GROUP) 97 | INSTALL := /usr/bin/install 98 | INSTALL_DIR := $(INSTALL) -d -g $(INSTALL_GROUP) -m $(INSTALL_EPERM) 99 | INSTALL_PROGRAM := $(INSTALL) -c -g $(INSTALL_GROUP) -m $(INSTALL_EPERM) 100 | INSTALL_DATA := $(INSTALL) -c -g $(INSTALL_GROUP) -m $(INSTALL_DPERM) 101 | STRIP := strip 102 | 103 | # Other standard tools 104 | # The tools below are supported by cygwin 105 | # !!! Do not use 'ar' and 'ranlib'. They are solely for unix machine 106 | # Current project is based on Windows Vista 107 | 108 | MAKE := make 109 | TAR := tar 110 | GREP := grep 111 | FIND := find 112 | PERL := perl 113 | GZIP := gzip -f 114 | CP := cp -f 115 | MV := mv -f 116 | RM := rm -f 117 | LN := ln -s 118 | TOUCH := touch 119 | MKDIR := mkdir -p 120 | AR := ar 121 | RANLIB := ranlib 122 | ECHO := echo 123 | SED := sed 124 | PWD := $(shell /bin/pwd) 125 | 126 | # ----------------------- 127 | # default macros for build 128 | 129 | # default values for macros (from command line) 130 | PLATFORM = linux64 131 | CONFIG = debug 132 | 133 | BUILD_PLATFORM := linux64 134 | BUILD_CONFIG := debug 135 | 136 | # 3: list of lib dirs 137 | LIB_DIRS = 138 | PRJ_LOCAL_LIB_INC = $(patsubst %,-L$(PRJ_ROOT_DIR)/%,$(LIB_DIRS)) 139 | PRJ_LOCAL_INC = $(patsubst %,-I$(PRJ_ROOT_DIR)/%,$(LIB_DIRS)) 140 | 141 | # optimzation options 142 | ifeq ($(CONFIG),debug) 143 | CFLAGS += -O0 144 | CXXFLAGS += -O0 145 | BUILD_CONFIG := debug 146 | else 147 | 148 | ifeq ($(CONFIG),optimize) 149 | CFLAGS += -O3 -DNDEBUG 150 | CXXFLAGS += -O3 -DNDEBUG 151 | BUILD_CONFIG := optimize 152 | else 153 | $(error "CONFIG can be either debug or optimize") 154 | 155 | endif 156 | endif 157 | 158 | # platform 159 | ifeq ($(PLATFORM),linux) 160 | BUILD_PLATFORM := linux 161 | CC += -m32 162 | CXX += -m32 163 | else 164 | BUILD_PLATFORM := linux64 165 | endif 166 | 167 | BUILD_DIR_ROOT := .build 168 | BUILD_DIR := $(BUILD_DIR_ROOT)/$(BUILD_PLATFORM)-$(BUILD_CONFIG) 169 | 170 | ifeq ($(SRCS),) 171 | SRCS := $(patsubst %.cc,%,$(wildcard *.cc)) 172 | SRCS += $(patsubst %.cpp,%,$(wildcard *.cpp)) 173 | SRCS += $(patsubst %.c,%,$(wildcard *.c)) 174 | else 175 | SRCS := $(patsubst %.cc,%,$(SRCS)) 176 | SRCS += $(patsubst %.cpp,%,$(SRCS)) 177 | SRCS += $(patsubst %.c,%,$(SRCS)) 178 | endif 179 | 180 | OBJS = $(SRCS:%=$(BUILD_DIR)/%.o) 181 | DEPS = $(SRCS:%=$(BUILD_DIR)/%.d) 182 | -------------------------------------------------------------------------------- /Makefile.rules: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ------------------------------------------------------------------------- 15 | # Makefile.rule: common build rules 16 | # 17 | # Author: yonghyun@google.com (Yonghyun Hwang) 18 | # ------------------------------------------------------------------------- 19 | 20 | # -------------------------------------------- 21 | # Magic part: please don't touch if you don't know what you are doing 22 | # -------------------------------------------- 23 | 24 | ifeq (,$(PRJ_ROOT_DIR)) 25 | $(error "PRJ_ROOT_DIR should be defined before including Makefile.rules file") 26 | endif 27 | include $(PRJ_ROOT_DIR)/Makefile.macros 28 | 29 | # ----------------------- 30 | # define utility functions 31 | define iterate_dirs 32 | set -e ; for d in $(2); do \ 33 | $(MAKE) -C $$d $(1); \ 34 | done 35 | endef 36 | 37 | PLATFORM = linux64 38 | CONFIG = debug 39 | 40 | define print_help 41 | $(ECHO) "Build targets for" $(1) 42 | $(ECHO) "" 43 | $(ECHO) " all build" $(1) 44 | $(ECHO) " clean clean all .o files" 45 | $(ECHO) " distclean clean all files generated during build" 46 | $(ECHO) " help display this message (default)" 47 | $(ECHO) "" 48 | $(ECHO) "Build options" 49 | $(ECHO) "" 50 | $(ECHO) " PLATFORM = [linux64|linux] default is linux64" 51 | $(ECHO) " CONFIG = [debug|optimize] default is debug" 52 | $(ECHO) "" 53 | $(ECHO) "Examples" 54 | $(ECHO) "" 55 | $(ECHO) " make all # debug linux64 build" 56 | $(ECHO) " make CONFIG=optimize all # optimize linux64 build" 57 | $(ECHO) " make PLATFORM=linux all # debug linux build" 58 | $(ECHO) "" 59 | endef 60 | 61 | PRT_PREFIX := "`hostname`[`date +'%m/%d %H:%M'`]::BUILD" 62 | define print_info_msg 63 | @$(ECHO) "$(PRT_PREFIX)[INFO]::" $(1) 64 | endef 65 | 66 | define print_warn_msg 67 | @$(ECHO) "$(PRT_PREFIX)[WARN]::" $(1) 68 | endef 69 | 70 | define platform_not_support 71 | help all install clean distclean test: 72 | $$(call print_info_msg, "platforms other than linux* are not supported for now") 73 | endef 74 | 75 | # actual body of generic build rules for all lib build 76 | define play_magic 77 | 78 | ifneq (,$$(filter linux%,$$(BUILD_PLATFORM))) 79 | 80 | ifeq ($$(LIB_NAME),) 81 | all: $$(EXE) 82 | $$(call print_info_msg, "$$(EXE) is created") 83 | else 84 | all: $$(LIB_NAME) 85 | $$(call print_info_msg, "lib$$(LIB_NAME).a is created") 86 | endif 87 | 88 | 89 | $$(BUILD_DIR): ; @$$(MKDIR) $$@ 90 | 91 | ifeq ($(SRCS),) 92 | $$(EXE): 93 | @$$(call iterate_dirs, all, $$(SUB_DIRS)) 94 | $$(call print_info_msg, "nothing to build here") 95 | 96 | $$(LIB_NAME): 97 | @$$(call iterate_dirs, all, $$(SUB_DIRS)) 98 | $$(call print_info_msg, "nothing to build here") 99 | 100 | else 101 | $$(EXE): $$(OBJS) 102 | @$$(call iterate_dirs, all, $$(SUB_DIRS)) 103 | @$$(CXX) -o $$@ $$^ $$(LOCAL_LIB) 104 | 105 | $$(LIB_NAME): CFLAGS+=-fPIC 106 | $$(LIB_NAME): CXXFLAGS+=-fPIC 107 | $$(LIB_NAME): $$(OBJS) 108 | @$$(call iterate_dirs, all, $$(SUB_DIRS)) 109 | @$$(AR) -crs lib$$@.a $$^ 110 | endif 111 | 112 | $$(OBJS): | $$(BUILD_DIR) 113 | 114 | clean: 115 | @$$(call iterate_dirs, $$@, $$(SUB_DIRS)) 116 | @$$(RM) -r $$(BUILD_DIR)/*.o 117 | ifneq ($$(LIB_NAME),) 118 | @$$(RM) -f lib$$(LIB_NAME).a 119 | endif 120 | $$(call print_info_msg, "$(1) is $$@ed") 121 | 122 | distclean: 123 | @$$(call iterate_dirs, $$@, $$(SUB_DIRS)) 124 | @$$(RM) -r $$(BUILD_DIR_ROOT) $$(EXE) 125 | ifneq ($$(LIB_NAME),) 126 | @$$(RM) -f lib$$(LIB_NAME).a 127 | endif 128 | $$(call print_info_msg, "$(1) is $$@ed") 129 | 130 | help: 131 | @$$(call print_help, "$(1)") 132 | 133 | else # platforms other than linux* are not supported 134 | $$(eval $$(call platform_not_support)) 135 | endif 136 | 137 | endef 138 | 139 | # default targets 140 | .PHONY: help all install clean distclean 141 | 142 | .DEFAULT_GOAL := help 143 | 144 | # compiling obj files & its dep 145 | $(BUILD_DIR)/%.o: %.c 146 | $(CC) $(CPPFLAGS) $(CFLAGS) $(PRJ_LOCAL_INC) $(LOCAL_CFLAGS) -c $< -o $@ 147 | 148 | $(BUILD_DIR)/%.o: %.cc 149 | $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(PRJ_LOCAL_INC) $(LOCAL_CXXFLAGS) -c $< -o $@ 150 | 151 | $(BUILD_DIR)/%.o: %.cpp 152 | $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(PRJ_LOCAL_INC) $(LOCAL_CXXFLAGS) -c $< -o $@ 153 | 154 | %.o: %.c 155 | $(call print_warn_msg, "should not fall here. critical error in build system") 156 | 157 | %.o: %.cc 158 | $(call print_warn_msg, "should not fall here. critical error in build system") 159 | 160 | %.o: %.cpp 161 | $(call print_warn_msg, "should not fall here. critical error in build system") 162 | 163 | # include auto gen-ed dep files 164 | -include $(DEPS) 165 | -------------------------------------------------------------------------------- /align_command.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #include "align_command.h" 19 | 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "auto_cleanup.h" 33 | #include "llvm/Support/raw_ostream.h" 34 | 35 | namespace 36 | { 37 | struct AlignArgs { 38 | char *diffed_file = nullptr; 39 | char *original_c = nullptr; 40 | char *patched_c = nullptr; 41 | char *patch = nullptr; 42 | char *suffix = nullptr; 43 | }; 44 | 45 | const char kAlignArgsDoc[] = " "; 46 | const char kAlignPrgDoc[] = "common align options:\n"; 47 | const struct argp_option kAlignOptions[] = { 48 | // name, key, arg, flags, doc, 49 | { /*name=*/"diffed_file", /*key=*/'d', /*arg=*/"DIFFED_FILE", 50 | /*flag=*/0, /*doc=*/"Filename for diffed file" }, 51 | { /*name=*/"patch", /*key=*/'p', /*arg=*/"PATCH", 52 | /*flag=*/0, /*doc=*/"Patch file" }, 53 | { /*name=*/"suffix", /*key=*/'s', /*arg=*/"SUFFIX", 54 | /*flag=*/0, /*doc=*/"Suffix for output file" }, 55 | { nullptr } 56 | }; 57 | 58 | constexpr std::string_view kDefaultAlignSuffix = "__aligned"; 59 | 60 | error_t ParseAlignOpt(int key, char *arg, struct argp_state *state) 61 | { 62 | AlignArgs *args = static_cast(state->input); 63 | 64 | switch (key) { 65 | case 'd': 66 | args->diffed_file = arg; 67 | break; 68 | case 'p': 69 | args->patch = arg; 70 | break; 71 | case 's': 72 | args->suffix = arg; 73 | break; 74 | case ARGP_KEY_ARG: 75 | if (!args->original_c) { 76 | args->original_c = arg; 77 | } else if (!args->patched_c) { 78 | args->patched_c = arg; 79 | } else { 80 | argp_usage(state); 81 | } 82 | break; 83 | case ARGP_KEY_END: 84 | if (!args->diffed_file) { 85 | llvm::errs() 86 | << "filename for diffed file is not given\n"; 87 | argp_usage(state); 88 | } 89 | if (!args->original_c) { 90 | llvm::errs() << " is not given\n"; 91 | argp_usage(state); 92 | } 93 | if (!args->patched_c) { 94 | llvm::errs() << " is not given\n"; 95 | argp_usage(state); 96 | } 97 | if (!args->patch) { 98 | llvm::errs() << "patch file is not given\n"; 99 | argp_usage(state); 100 | } 101 | break; 102 | default: 103 | return ARGP_ERR_UNKNOWN; 104 | } 105 | return 0; 106 | } 107 | 108 | // Reads in_file and skips lines till read line starts with the given marker. Once this 109 | // hits the marker, it returns the marked line. If it hits the end of file or stopper, it 110 | // returns "". 111 | std::string SkipToMarker(std::fstream &in_file, std::string_view marker, 112 | std::string_view stopper = "") 113 | { 114 | const std::regex marker_regex(marker.data()); 115 | const std::regex stopper_regex(stopper.data()); 116 | std::string line; 117 | while (std::getline(in_file, line)) { 118 | if (std::regex_search(line, marker_regex)) { 119 | return line; 120 | } 121 | if (!stopper.empty() && 122 | std::regex_search(line, stopper_regex)) { 123 | return ""; 124 | } 125 | } 126 | return ""; 127 | } 128 | 129 | void CopyLines(std::fstream &in_file, std::fstream &out_file, size_t lines) 130 | { 131 | std::string line; 132 | for (size_t i = 0; i < lines && std::getline(in_file, line); i++) { 133 | out_file << line << std::endl; 134 | } 135 | } 136 | 137 | void AddEmptyLines(std::fstream &out_file, size_t lines) 138 | { 139 | out_file << std::string(lines, '\n'); 140 | } 141 | 142 | std::pair 143 | GetOffsetLinesPair(const std::string &pair) 144 | { 145 | // format of pair: [-+]${line#},${lines_changed} 146 | const auto pos = pair.find(','); 147 | std::string offset = pair.substr(1, pos - 1); 148 | std::string lines = pair.substr(pos + 1); 149 | 150 | return { std::stoi(offset), std::stoi(lines) }; 151 | } 152 | 153 | // offset in Patch is absolute value from the file start. convert it to relative offset 154 | // relative to the last changed. Do this because empty lines are added. 155 | void ConvertToRelativeOffset(std::vector *patches) 156 | { 157 | size_t last_patch_line = 0; 158 | const size_t patch_size = patches->size(); 159 | for (size_t i = 0; i < patch_size; i++) { 160 | auto [offset, lines] = (*patches)[i]; 161 | (*patches)[i] = { offset - last_patch_line, lines }; 162 | last_patch_line = offset; 163 | } 164 | } 165 | 166 | std::tuple, 167 | /*patched*/ std::vector, 168 | /*patch context*/ std::vector > 169 | ParsePatchFile(const std::string &patch, const std::string &original) 170 | noexcept(false) 171 | { 172 | std::fstream file(patch.data(), std::ios::in); 173 | if (!file.is_open()) { 174 | throw std::error_code{ errno, std::system_category() }; 175 | } 176 | AutoCleanup fd_close([&file_ = file]() { file_.close(); }); 177 | 178 | // format: diff -.* ${file1} ${file2} 179 | // NOTE: patch file can be generated without using 'git diff' 180 | // command. So, regular expression for matching. 181 | std::vector original_patch; 182 | std::vector patched_patch; 183 | std::vector patch_context; 184 | 185 | static constexpr std::string_view diff_head = "^diff -.*"; 186 | std::string diff_file_head = std::string(diff_head) + original + ".*"; 187 | std::string line = SkipToMarker(file, diff_file_head); 188 | if (line.empty()) { 189 | // This happens when .c file includes "changed" header file. 190 | return {original_patch, patched_patch, patch_context}; 191 | } 192 | 193 | size_t offset, lines; 194 | std::string token; 195 | for (line = SkipToMarker(file, "^@@", diff_head); line != ""; 196 | line = SkipToMarker(file, "^@@", diff_head)) { 197 | // format: @@ -${line#},${lines_changed} +${line#},{lines_changed} @@ ... 198 | // e.g.,: @@ -37,16 +37,17 @@ ... 199 | 200 | std::istringstream iss(line); 201 | std::getline(iss, token, ' '); // @@. so simply skip 202 | 203 | std::getline(iss, token, ' '); // -${line#},${lines_changed} 204 | std::tie(offset, lines) = GetOffsetLinesPair(token); 205 | original_patch.emplace_back(offset, lines); 206 | 207 | std::getline(iss, token, ' '); // +${line#},${lines_changed} 208 | std::tie(offset, lines) = GetOffsetLinesPair(token); 209 | patched_patch.emplace_back(offset, lines); 210 | 211 | // Then, there are few lines for patch context before actual changes are 212 | // specified. 213 | size_t i; 214 | for (i = 0; std::getline(file, line); i++) { 215 | if (line.find('-') == 0 || line.find('+') == 0) { 216 | break; 217 | } 218 | } 219 | // the line of context at ${line#}. so, -1 from the context 220 | patch_context.push_back(i ? i - 1 : 0); 221 | } 222 | 223 | ConvertToRelativeOffset(&original_patch); 224 | ConvertToRelativeOffset(&patched_patch); 225 | 226 | return { original_patch, patched_patch, patch_context }; 227 | } 228 | 229 | } // namespace 230 | 231 | AlignCommand::AlignCommand(int argc, char **argv) noexcept(false) 232 | { 233 | if (argc < 1) { 234 | throw std::error_code{ ErrorCode::NOT_ENOUGH_ARGS }; 235 | } 236 | 237 | AlignArgs arguments; 238 | struct argp argp = { /*options=*/kAlignOptions, 239 | /*parser=*/ParseAlignOpt, 240 | /*args_doc=*/kAlignArgsDoc, 241 | /*args_doc=*/kAlignPrgDoc }; 242 | 243 | // First argument is a command, 'align' and it's already consumed. So, 244 | // argv[0] = argv[0] + argv[1] to let others used for options. 245 | std::string command = std::string(argv[0]) + " " + argv[1]; 246 | --argc; 247 | ++argv; 248 | argv[0] = const_cast(command.c_str()); 249 | argp_parse(&argp, argc, argv, /*flags=*/0, 250 | /*arg_index=*/nullptr, /*input=*/&arguments); 251 | 252 | diffed_file_ = arguments.diffed_file; 253 | original_filename_ = arguments.original_c; 254 | patched_filename_ = arguments.patched_c; 255 | patch_filename_ = arguments.patch; 256 | output_suffix_ = 257 | arguments.suffix ? arguments.suffix : kDefaultAlignSuffix; 258 | } 259 | 260 | std::error_code AlignCommand::Run() 261 | { 262 | auto [original, patched, context] = 263 | ParsePatchFile(patch_filename_, diffed_file_); 264 | 265 | AlignFile(original_filename_, original, patched, context); 266 | AlignFile(patched_filename_, patched, original, context); 267 | 268 | return Command::ErrorCode::NO_ERROR; 269 | } 270 | 271 | void AlignCommand::AlignFile(std::string_view filename, 272 | const std::vector &from, 273 | const std::vector &to, 274 | const std::vector &context) 275 | { 276 | std::fstream in_file(filename.data(), std::ios::in); 277 | if (!in_file.is_open()) { 278 | throw std::error_code{ errno, std::system_category() }; 279 | } 280 | AutoCleanup in_fd_close([&in_file_ = in_file]() { in_file_.close(); }); 281 | 282 | std::fstream out_file(std::string(filename) + output_suffix_, 283 | std::ios::out); 284 | if (!out_file.is_open()) { 285 | throw std::error_code{ errno, std::system_category() }; 286 | } 287 | AutoCleanup out_fd_close( 288 | [&out_file_ = out_file]() { out_file_.close(); }); 289 | 290 | size_t patch_size = from.size(); 291 | std::string line; 292 | for (size_t i = 0; i < patch_size; i++) { 293 | // NOTE: *offset is relative offset from the last change 294 | auto [from_file_offset, from_file_lines] = from[i]; 295 | auto [to_file_offset, to_file_lines] = to[i]; 296 | CopyLines(in_file, out_file, from_file_offset); 297 | if (from_file_lines < to_file_lines) { 298 | // Skip over patch contexts and add empty lines after the contexts. 299 | CopyLines(in_file, out_file, context[i]); 300 | AddEmptyLines(out_file, 301 | to_file_lines - from_file_lines); 302 | } 303 | } 304 | // Copy to the end 305 | CopyLines(in_file, out_file, std::numeric_limits::max()); 306 | } 307 | -------------------------------------------------------------------------------- /align_command.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #ifndef ALIGN_COMMAND_H_ 19 | #define ALIGN_COMMAND_H_ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "command.h" 27 | 28 | // This class implements align command for kernel livepatch generation. The 'align' 29 | // command inputs three files, .patch, original.c, and patched.c. The command adds empty 30 | // lines to original.c and/or patched.c to make their __LINE__ macros aligned 31 | // respectively. Name of output file has a given output_suffix_. This is required to avoid 32 | // false-positive diffs. 33 | // 34 | // e.g., .patch removes 10 lines from original.c and adds 20 lines into .patched.c. Then, 35 | // c's __LINE__ macros used after patch's change is translated onto different # in a final 36 | // ELF binary and LLVM IR file. This, in turn, makes unexpected diffs for kernel livepatch 37 | // generation. 38 | class AlignCommand : public Command { 39 | public: 40 | // struct Patch contains "relative" offset from the previous patched lines and how 41 | // many lines are changed from the offset. 42 | struct Patch { 43 | Patch(size_t offset, size_t lines) 44 | : offset(offset), lines(lines) 45 | { 46 | } 47 | ~Patch() = default; 48 | size_t offset = 0; 49 | size_t lines = 0; 50 | }; 51 | 52 | static constexpr std::string_view kCommandName = "align"; 53 | 54 | AlignCommand(int argc, char **argv) noexcept(false); 55 | ~AlignCommand() override = default; 56 | 57 | // Don't allow copy. 58 | AlignCommand(const AlignCommand &rhs) = delete; 59 | AlignCommand &operator=(const AlignCommand &rhs) = delete; 60 | 61 | // Runs align command for __LINE__ macro in original.c and patched.c. 62 | std::error_code Run() override; 63 | 64 | void AlignFile(std::string_view filename, 65 | const std::vector &from, 66 | const std::vector &to, 67 | const std::vector &context); 68 | 69 | private: 70 | // name of original diffed file. this is used as a marker while parsing .patch 71 | // file. 72 | std::string diffed_file_; 73 | std::string original_filename_; 74 | std::string patched_filename_; 75 | std::string patch_filename_; 76 | std::string output_suffix_; 77 | }; 78 | 79 | #endif // ALIGN_COMMAND_H_ 80 | -------------------------------------------------------------------------------- /auto_cleanup.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #ifndef AUTO_CLEANUP_H_ 19 | #define AUTO_CLEANUP_H_ 20 | 21 | #include 22 | 23 | // This class is a helper class to implement RAII (Resource Acquisition Is 24 | // Initialization) for non class object, such as file descriptor by 25 | // open(). When the object for this class gets out of scope, it checks 26 | // internal flag, enabled_, and runs cleanup_ function. 27 | class AutoCleanup final { 28 | public: 29 | AutoCleanup(std::function cleanup) 30 | : cleanup_(std::move(cleanup)) 31 | { 32 | } 33 | ~AutoCleanup() 34 | { 35 | if (enabled_) { 36 | cleanup_(); 37 | } 38 | } 39 | 40 | // Don't allow copy. 41 | AutoCleanup(const AutoCleanup &rhs) = delete; 42 | AutoCleanup &operator=(const AutoCleanup &rhs) = delete; 43 | 44 | void Disable() 45 | { 46 | enabled_ = false; 47 | } 48 | 49 | private: 50 | const std::function cleanup_; 51 | bool enabled_ = true; 52 | }; 53 | 54 | #endif // AUTO_CLEANUP_H_ 55 | -------------------------------------------------------------------------------- /command.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #include "command.h" 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "align_command.h" 25 | #include "diff_command.h" 26 | #include "gen_command.h" 27 | #include "fixup_command.h" 28 | #include "llvm/Support/raw_ostream.h" 29 | 30 | namespace 31 | { 32 | struct CommandErrorCategory : std::error_category { 33 | const char *name() const noexcept override 34 | { 35 | return "livepatch"; 36 | } 37 | std::string message(int ev) const override; 38 | }; 39 | 40 | std::string CommandErrorCategory::message(int ev) const 41 | { 42 | const char *msg = nullptr; 43 | 44 | switch (static_cast(ev)) { 45 | case Command::ErrorCode::INVALID_COMMAND: 46 | msg = "invalid command"; 47 | break; 48 | case Command::ErrorCode::NOT_ENOUGH_ARGS: 49 | msg = "not enough arguments"; 50 | break; 51 | case Command::ErrorCode::INVALID_LLVM_FILE: 52 | msg = "invalid LLVM file"; 53 | break; 54 | case Command::ErrorCode::DIFF_FAILED: 55 | msg = "diff failed"; 56 | break; 57 | case Command::ErrorCode::FILE_OPEN_FAILED: 58 | msg = "failed to open file"; 59 | break; 60 | case Command::ErrorCode::INVALID_PATCH_FILE: 61 | msg = "invalid patch file"; 62 | break; 63 | case Command::ErrorCode::NOTHING_TO_PATCH: 64 | msg = "nothing to patch"; 65 | break; 66 | case Command::ErrorCode::SYM_FIND_FAILED: 67 | msg = "symbol not found in thin archive"; 68 | break; 69 | case Command::ErrorCode::INVALID_SYM_MAP: 70 | msg = "invalid symbol map file"; 71 | break; 72 | case Command::ErrorCode::ALIAS_FIND_FAILED: 73 | msg = "alias not found in symbol map"; 74 | break; 75 | case Command::ErrorCode::NO_SYM_MAP: 76 | msg = "no symbol map file to resolve symbol alias"; 77 | break; 78 | default: 79 | msg = "unrecognized error"; 80 | break; 81 | } 82 | 83 | return std::string(name()) + ": " + msg; 84 | } 85 | 86 | const CommandErrorCategory _CommandErrorCategory{}; 87 | }; // namespace 88 | 89 | std::unique_ptr Command::Create(int argc, char **argv) noexcept(false) 90 | { 91 | const char *exec_name = argv[0]; 92 | if (const char *slash = rindex(exec_name, '/')) { 93 | exec_name = slash + 1; 94 | } 95 | 96 | if (argc < 2) { 97 | return std::make_unique(exec_name); 98 | } 99 | 100 | std::string command = argv[1]; 101 | if (command == DiffCommand::kCommandName) { 102 | return std::make_unique(argc, argv); 103 | } else if (command == GenCommand::kCommandName) { 104 | return std::make_unique(argc, argv); 105 | } else if (command == FixupCommand::kCommandName) { 106 | return FixupCommand::Create(argc, argv); 107 | } else if (command == AlignCommand::kCommandName) { 108 | return std::make_unique(argc, argv); 109 | } else if (command == UsageCommand::kCommandName) { 110 | return std::make_unique(exec_name); 111 | } 112 | 113 | throw std::error_code{ ErrorCode::INVALID_COMMAND }; 114 | } 115 | 116 | std::error_code UsageCommand::Run() 117 | { 118 | llvm::outs() 119 | << "usage: " << cmd_ 120 | << " []\n" 121 | "Utility for kernel livepatch generation\n" 122 | "\n" 123 | "Available commands:\n" 124 | "\n" 125 | "align align __LINE__ for original.c and patched.c for a given .patch\n" 126 | " by adding empty lines\n" 127 | "diff diff two LLVM IR files and output a new LLVM IR file\n" 128 | " that distills changed/new functions and global variables\n" 129 | "fixup rename UND symbols and create a relocation section for klp.\n" 130 | "gen generate livepatch wrapper, makefile, and linker script\n"; 131 | 132 | return {}; 133 | } 134 | 135 | std::error_code make_error_code(Command::ErrorCode e) 136 | { 137 | return { static_cast(e), _CommandErrorCategory }; 138 | } 139 | -------------------------------------------------------------------------------- /command.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #ifndef COMMAND_H_ 19 | #define COMMAND_H_ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | // This class implements a factory to parse command line arguments and 26 | // return command object based on the user arguments. It's an abstract 27 | // class to provide an interface for user command. Hence, Any commands for 28 | // livepatch should inherit from this class. 29 | struct Command { 30 | // This enum class is used to create std::error_code for command line arguments to 31 | // throw exception. 32 | enum class ErrorCode { 33 | NO_ERROR = 0, 34 | INVALID_COMMAND = 1, 35 | NOT_ENOUGH_ARGS = 2, 36 | INVALID_LLVM_FILE = 3, 37 | DIFF_FAILED = 4, 38 | FILE_OPEN_FAILED = 5, 39 | INVALID_PATCH_FILE = 6, 40 | NOTHING_TO_PATCH = 7, 41 | SYM_FIND_FAILED = 8, 42 | INVALID_SYM_MAP = 9, 43 | ALIAS_FIND_FAILED = 10, 44 | NO_SYM_MAP = 11, 45 | }; 46 | 47 | virtual ~Command() = default; 48 | 49 | // Creates a new command instance with a given command line arguments. 50 | static std::unique_ptr Create(int argc, char **argv) 51 | noexcept(false); 52 | 53 | // Run a user command. 54 | virtual std::error_code Run() = 0; 55 | }; 56 | 57 | // This class implement a simple command to print out command usage. 58 | class UsageCommand : public Command { 59 | public: 60 | static constexpr std::string_view kCommandName = "help"; 61 | 62 | UsageCommand(const char *cmd) noexcept : cmd_(cmd) 63 | { 64 | } 65 | 66 | std::error_code Run() override; 67 | 68 | private: 69 | const char *cmd_; 70 | }; 71 | 72 | namespace std 73 | { 74 | template <> struct is_error_code_enum : true_type { 75 | }; 76 | } // namespace std 77 | std::error_code make_error_code(Command::ErrorCode); 78 | 79 | #endif // COMMAND_H_ 80 | -------------------------------------------------------------------------------- /create-package: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Copyright 2021 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # Author: yonghyun@google.com (Yonghyun Hwang) 18 | # 19 | # script to generate a tarball, package, for kernel livepatch 20 | 21 | #------------------------------------------------------------- 22 | # Shell setting 23 | #------------------------------------------------------------- 24 | set -E 25 | 26 | #------------------------------------------------------------- 27 | # Global variables 28 | #------------------------------------------------------------- 29 | declare -r G_CREATE_PACKAGE_CMD="$0" 30 | declare -r G_CREATE_PACKAGE_CMDLINE="$0 $@" 31 | declare -r G_CREATE_PACKAGE_PATH=$(dirname $(readlink -f "${G_CREATE_PACKAGE_CMD}")) 32 | declare -r G_TMP_DIR="$(mktemp -d -t livepatch.XXXXXXXXXX)" 33 | declare -r G_PKG_ROOT="${G_TMP_DIR}/pkg" 34 | 35 | declare G_DBG_DIR="" 36 | declare G_KDIR="" 37 | 38 | declare G_BUILDINFO_FILE="" 39 | declare G_KLP_FILE="" 40 | declare G_PATCH_FILE="" 41 | declare G_PKG_FILE="" 42 | 43 | #------------------------------------------------------------- 44 | # Include library 45 | #------------------------------------------------------------- 46 | source "${G_CREATE_PACKAGE_PATH}/libutil.bash" "create-package" 47 | 48 | #------------------------------------------------------------- 49 | # Function definitions 50 | #------------------------------------------------------------- 51 | function cleanup() 52 | { 53 | local errCode="${1:-}" 54 | local lineNum="${2:-}" 55 | 56 | [[ ${errCode} == 0 ]] || \ 57 | util::log_error "trap at line: ${lineNum}, with error:${errCode}." 58 | 59 | if [[ -n "${G_DEBUG_DIR}" ]]; then 60 | mv -f "${G_PKG_ROOT}" "${G_DEBUG_DIR}" 61 | G_DEBUG_DIR="" 62 | fi 63 | rm -fr "${G_TMP_DIR}" 64 | 65 | exit "${errCode}" 66 | } 67 | # enable exit trap for debugging purpose 68 | trap 'cleanup $? ${LINENO}' ERR INT TERM EXIT 69 | 70 | function print_usage() 71 | { 72 | cat <| "${FILENAME}" <> "${FILENAME}" 207 | 208 | cat >> "${FILENAME}" <> "${SCRIPT_NAME}" <> "${SCRIPT_NAME}" < \${LIVEPATCH_ENABLED}" 2>&1)" 279 | [[ -z "\${out}" ]] && break 280 | 281 | i=\$((i+1)) 282 | if [[ \${i} -eq 3 ]]; then 283 | util::log_error "Failed to disable the livepatch, \${MODULE_NAME}." 284 | exit 1 285 | else 286 | util::log_info "Retrying..." 287 | sleep 2s 288 | fi 289 | done 290 | util::log_info "Livepatch, \${MODULE_NAME}, is disabled" 291 | 292 | util::log_info "Unloading Livepatch, \${MODULE_NAME}" 293 | i=0 294 | while sleep 2s; do 295 | rmmod "\${MODULE_NAME}" && break 296 | i=\$((i+1)) 297 | if [[ \${i} -eq 3 ]]; then 298 | util::log_error "Failed to unload the livepatch, \${MODULE_NAME}." 299 | exit 1 300 | else 301 | util::log_info "Retrying unloading..." 302 | fi 303 | done 304 | 305 | util::log_ok "Livepatch, \${MODULE_NAME}, is unloaded" 306 | exit 0 307 | EOF 308 | chmod a+x "${SCRIPT_NAME}" 309 | } 310 | 311 | function generate_script_check() 312 | { 313 | local -r SCRIPT_NAME="${1}" 314 | generate_script_header "${SCRIPT_NAME}" 315 | cat >> "${SCRIPT_NAME}" < ../../lib/patches/${KERNEL_RELEASE}/${name}/apply 335 | #./lib/patches/${KERNEL_RELEASE}/${name}/apply 336 | #./lib/patches/${KERNEL_RELEASE}/${name}/check 337 | #./lib/patches/${KERNEL_RELEASE}/${name}/revert 338 | #./lib/patches/${KERNEL_RELEASE}/${name}/patch 339 | #./lib/patches/${KERNEL_RELEASE}/${name}/buildinfo 340 | #./lib/patches/${KERNEL_RELEASE}/${name}/livepatch-${name}.ko 341 | # 342 | # This function assumes that `cwd` is ${kdir}. 343 | function package_build() 344 | { 345 | util::log_info "Building livepatch package." 346 | local -r KERNEL_RELEASE="$(strings "init/version.o" | awk '/^Linux version/ { print $3 }')" 347 | local -r PATCH_NAME="${1}" 348 | 349 | local -r SCRIPT_PATH="lib/patches/${KERNEL_RELEASE}/$(util::get_livepatch_name "${PATCH_NAME}")" 350 | local -r SCRIPT_FULL_PATH="${G_PKG_ROOT}/${SCRIPT_PATH}" 351 | if ! mkdir -p "${SCRIPT_FULL_PATH}"; then 352 | util::error "Failed to create a path for livepatch script." 353 | fi 354 | 355 | cp -f "${G_KLP_FILE}" "${SCRIPT_FULL_PATH}" 356 | cp -f "${G_PATCH_FILE}" "${SCRIPT_FULL_PATH}/patch" 357 | cp -f "${G_BUILDINFO_FILE}" "${SCRIPT_FULL_PATH}/buildinfo" 358 | generate_script_apply "${SCRIPT_FULL_PATH}/apply" 359 | generate_script_revert "${SCRIPT_FULL_PATH}/revert" 360 | generate_script_check "${SCRIPT_FULL_PATH}/check" 361 | 362 | # Create symlink to apply livepatch from /etc/rcS.d 363 | local -r INIT_PATH="etc/rcS.d" 364 | local -r INIT_FULL_PATH="${G_PKG_ROOT}/${INIT_PATH}" 365 | if ! mkdir -p "${INIT_FULL_PATH}"; then 366 | util::error "Failed to create a path for init.d script." 367 | fi 368 | ln -r -s "${SCRIPT_FULL_PATH}/apply" \ 369 | "${INIT_FULL_PATH}/S165kernelpatch-${KERNEL_RELEASE}-$(util::get_livepatch_name "${PATCH_NAME}")" 370 | 371 | # Set correct mode bits 372 | chmod -R u=rwX,go=rX "${G_PKG_ROOT}" 373 | 374 | # Create a package tarball. 375 | tar -C "${G_PKG_ROOT}" --owner=root --group=root -cJ -f "${G_PKG_FILE}" \ 376 | "${SCRIPT_PATH}" "${INIT_PATH}" || util::error "tar Failed" 377 | util::log_ok "Created ${G_PKG_FILE}" 378 | } 379 | 380 | #------------------------------------------------------------- 381 | # Main starts here 382 | #------------------------------------------------------------- 383 | parse_options "$@" 384 | 385 | validate_prepare 386 | 387 | package_build "${G_PATCH_FILE}" 388 | 389 | exit 0 390 | -------------------------------------------------------------------------------- /diff_command.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #ifndef DIFF_COMMAND_H_ 19 | #define DIFF_COMMAND_H_ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "command.h" 27 | #include "llvm/IR/Module.h" 28 | 29 | // This class implements diff command for kernel livepatch generation. The 30 | // 'diff' command inputs two LLVM IR files, original.ll and patched.ll, and 31 | // distills differences between them for C functions and global 32 | // variables. The 'diff' command outputs an LLVM IR file with patched/new C 33 | // functions and global variables in it. 34 | class DiffCommand : public Command { 35 | public: 36 | static constexpr std::string_view kCommandName = "diff"; 37 | 38 | DiffCommand(int argc, char **argv) noexcept(false); 39 | ~DiffCommand() override = default; 40 | 41 | // Don't allow copy. 42 | DiffCommand(const DiffCommand &rhs) = delete; 43 | DiffCommand &operator=(const DiffCommand &rhs) = delete; 44 | 45 | // Runs diff command and outputs an LLVM IR file with patched/new C functions and 46 | // global variables in it. 47 | std::error_code Run() override; 48 | 49 | // Diffs two LLVM modules and returns a LLVM module that distills difference 50 | // between them. 51 | std::unique_ptr 52 | DistillDiff(std::unique_ptr original, 53 | std::unique_ptr patched); 54 | 55 | private: 56 | std::string original_filename_; 57 | std::string patched_filename_; 58 | std::string base_dir_; 59 | bool quiet_mode_ = false; 60 | }; 61 | 62 | #endif // DIFF_COMMAND_H_ 63 | -------------------------------------------------------------------------------- /docs/code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of 9 | experience, education, socio-economic status, nationality, personal appearance, 10 | race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or reject 41 | comments, commits, code, wiki edits, issues, and other contributions that are 42 | not aligned to this Code of Conduct, or to ban temporarily or permanently any 43 | contributor for other behaviors that they deem inappropriate, threatening, 44 | offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | This Code of Conduct also applies outside the project spaces when the Project 56 | Steward has a reasonable belief that an individual's behavior may have a 57 | negative impact on the project or its community. 58 | 59 | ## Conflict Resolution 60 | 61 | We do not believe that all conflict is bad; healthy debate and disagreement 62 | often yield positive results. However, it is never okay to be disrespectful or 63 | to engage in behavior that violates the project’s code of conduct. 64 | 65 | If you see someone violating the code of conduct, you are encouraged to address 66 | the behavior directly with those involved. Many issues can be resolved quickly 67 | and easily, and this gives people more control over the outcome of their 68 | dispute. If you are unable to resolve the matter for any reason, or if the 69 | behavior is threatening or harassing, report it. We are dedicated to providing 70 | an environment where participants feel welcome and safe. 71 | 72 | Reports should be directed to *[PROJECT STEWARD NAME(s) AND EMAIL(s)]*, the 73 | Project Steward(s) for *[PROJECT NAME]*. It is the Project Steward’s duty to 74 | receive and address reported violations of the code of conduct. They will then 75 | work with a committee consisting of representatives from the Open Source 76 | Programs Office and the Google Open Source Strategy team. If for any reason you 77 | are uncomfortable reaching out to the Project Steward, please email 78 | opensource@google.com. 79 | 80 | We will investigate every complaint, but you may not receive a direct response. 81 | We will use our discretion in determining when and how to follow up on reported 82 | incidents, which may range from not taking action to permanent expulsion from 83 | the project and project-sponsored spaces. We will notify the accused of the 84 | report and provide them an opportunity to discuss it before any action is taken. 85 | The identity of the reporter will be omitted from the details of the report 86 | supplied to the accused. In potentially harmful situations, such as ongoing 87 | harassment or threats to anyone's safety, we may take action without notice. 88 | 89 | ## Attribution 90 | 91 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4, 92 | available at 93 | https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 94 | -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code Reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows [Google's Open Source Community 28 | Guidelines](https://opensource.google/conduct/). 29 | -------------------------------------------------------------------------------- /elf_bin.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #include "elf_bin.h" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | #include "auto_cleanup.h" 30 | #include "elf_error.h" 31 | #include "llvm/Support/raw_ostream.h" 32 | 33 | namespace 34 | { 35 | constexpr size_t kSectionFlagRelaLivepatch = 0x00100000; 36 | 37 | void throw_gelf_error() 38 | { 39 | throw std::error_code{ static_cast(elf_errno()) }; 40 | } 41 | } // namespace 42 | 43 | ElfBin::ElfBin(std::string_view elf_filename) noexcept(false) 44 | { 45 | elf_fd_ = open(elf_filename.data(), O_RDWR, 0); 46 | if (elf_fd_ < 0) { 47 | throw std::error_code{ errno, std::system_category() }; 48 | } 49 | AutoCleanup elf_fd_close([elf_fd = elf_fd_]() { close(elf_fd); }); 50 | 51 | if (elf_version(EV_CURRENT) == EV_NONE) { 52 | throw_gelf_error(); 53 | } 54 | 55 | elf_ = elf_begin(elf_fd_, ELF_C_RDWR, nullptr); 56 | if (!elf_) { 57 | throw_gelf_error(); 58 | } 59 | 60 | elf_fd_close.Disable(); 61 | } 62 | 63 | ElfBin::~ElfBin() 64 | { 65 | elf_end(elf_); 66 | close(elf_fd_); 67 | } 68 | 69 | Elf_Data *ElfBin::GetElfSectionData(size_t sec_idx) noexcept(false) 70 | { 71 | Elf_Scn *scn = elf_getscn(elf_, sec_idx); 72 | if (scn == nullptr) { 73 | throw_gelf_error(); 74 | } 75 | Elf_Data *elf_data = elf_getdata(scn, nullptr); 76 | if (elf_data == nullptr) { 77 | throw_gelf_error(); 78 | } 79 | 80 | return elf_data; 81 | } 82 | 83 | std::string_view ElfBin::SectionName(size_t sec_idx) 84 | { 85 | Elf_Scn *scn = elf_getscn(elf_, sec_idx); 86 | if (scn == nullptr) { 87 | throw_gelf_error(); 88 | } 89 | 90 | GElf_Shdr section_header; 91 | if (gelf_getshdr(scn, §ion_header) == nullptr) { 92 | throw_gelf_error(); 93 | } 94 | 95 | return elf_strptr(elf_, GetStringSectionIndex(), 96 | section_header.sh_name); 97 | } 98 | 99 | std::string ElfBin::ModName() 100 | { 101 | static constexpr std::string_view kModInfoSecName = ".modinfo"; 102 | static constexpr std::string_view kModNameTag = "name="; 103 | 104 | // if this for-loop fails to find mod section, it throws exception in 105 | // the loop. 106 | size_t i; 107 | for (i = 0; SectionName(i) != kModInfoSecName; i++) 108 | ; 109 | 110 | Elf_Data *elf_data = GetElfSectionData(i); 111 | std::string_view mod_info(static_cast(elf_data->d_buf), 112 | elf_data->d_size); 113 | 114 | // search for "name=${kernel_module_name}" 115 | size_t mod_name_start = mod_info.find(kModNameTag) + kModNameTag.size(); 116 | size_t mod_name_end = mod_info.find('\0', mod_name_start); 117 | return std::string( 118 | mod_info.substr(mod_name_start, mod_name_end - mod_name_start)); 119 | } 120 | 121 | void ElfBin::UpdateSection(size_t sec_idx, void *data, size_t size) 122 | noexcept(false) 123 | { 124 | Elf_Data *elf_data = GetElfSectionData(sec_idx); 125 | 126 | elf_data->d_buf = data; 127 | elf_data->d_size = size; 128 | 129 | if (!elf_flagdata(elf_data, ELF_C_SET, ELF_F_DIRTY)) { 130 | throw_gelf_error(); 131 | } 132 | } 133 | 134 | std::unique_ptr > ElfBin::GetSection(size_t sec_idx) 135 | { 136 | Elf_Data *elf_data = GetElfSectionData(sec_idx); 137 | char *d_buf = static_cast(elf_data->d_buf); 138 | return std::make_unique >(d_buf, 139 | d_buf + elf_data->d_size); 140 | } 141 | 142 | size_t ElfBin::GetStringSectionIndex() noexcept(false) 143 | { 144 | size_t idx; 145 | if (elf_getshdrstrndx(elf_, &idx)) { 146 | throw_gelf_error(); 147 | } 148 | 149 | return idx; 150 | } 151 | 152 | void ElfBin::UpdateRela(size_t section_id, 153 | std::vector *rela_vector) 154 | noexcept(false) 155 | { 156 | GElf_Shdr rela_header = {}; 157 | Elf_Scn *scn = nullptr; 158 | while ((scn = elf_nextscn(elf_, scn))) { 159 | gelf_getshdr(scn, &rela_header); 160 | if (rela_header.sh_type == SHT_RELA && 161 | rela_header.sh_info == section_id) 162 | break; 163 | } 164 | if (scn == nullptr) { 165 | throw std::error_code{ ElfErrorCode::RELA_SECTION_NOT_FOUND }; 166 | } 167 | 168 | Elf_Data *data = elf_getdata(scn, nullptr); 169 | if (!data) { 170 | throw_gelf_error(); 171 | } 172 | 173 | if (!elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY)) { 174 | throw_gelf_error(); 175 | } 176 | 177 | data->d_buf = rela_vector->data(); 178 | data->d_size = rela_vector->size() * sizeof(ElfRela::RelaEntry); 179 | 180 | rela_header.sh_size = rela_vector->size() * sizeof(ElfRela::RelaEntry); 181 | 182 | if (!gelf_update_shdr(scn, &rela_header)) { 183 | throw_gelf_error(); 184 | } 185 | } 186 | 187 | void ElfBin::CreateKlpRela(size_t section_id, size_t symtab_id, 188 | size_t section_name, 189 | std::vector *rela_vector) 190 | noexcept(false) 191 | { 192 | Elf_Scn *scn = elf_newscn(elf_); 193 | if (!scn) { 194 | throw_gelf_error(); 195 | } 196 | 197 | Elf_Data *data = elf_newdata(scn); 198 | if (!data) { 199 | throw_gelf_error(); 200 | } 201 | 202 | if (!elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY)) { 203 | throw_gelf_error(); 204 | } 205 | 206 | data->d_type = ELF_T_RELA; 207 | data->d_buf = rela_vector->data(); 208 | data->d_size = rela_vector->size() * sizeof(ElfRela::RelaEntry); 209 | 210 | GElf_Shdr shdr; 211 | if (!gelf_getshdr(scn, &shdr)) { 212 | throw_gelf_error(); 213 | } 214 | 215 | shdr.sh_name = section_name; 216 | // id for text section that needs relocation. 217 | shdr.sh_info = section_id; 218 | // id for symbol table. 219 | shdr.sh_link = symtab_id; 220 | shdr.sh_type = SHT_RELA; 221 | shdr.sh_entsize = sizeof(ElfRela::RelaEntry); 222 | shdr.sh_size = rela_vector->size() * shdr.sh_entsize; 223 | shdr.sh_addralign = 8; 224 | shdr.sh_flags = kSectionFlagRelaLivepatch | SHF_INFO_LINK | SHF_ALLOC; 225 | 226 | if (!gelf_update_shdr(scn, &shdr)) { 227 | throw_gelf_error(); 228 | } 229 | } 230 | 231 | void ElfBin::ElfUpdate() noexcept(false) 232 | { 233 | if (elf_update(elf_, ELF_C_WRITE) < 0) { 234 | throw_gelf_error(); 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /elf_bin.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #ifndef ELF_BIN_H_ 19 | #define ELF_BIN_H_ 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "elf_rela.h" 29 | #include "elf_symbol.h" 30 | 31 | // This class is an adapter class to abstract away gelf library. This 32 | // parses elf binary to handle it. The class can create iterator for elf 33 | // symbols and create non-standard relocation section for livepatch 34 | // generation. Note that this class is specially designed for kernel 35 | // livepatch generation. Hence, it implements minimum set of operations for 36 | // manipulating elf binary. 37 | class ElfBin final { 38 | public: 39 | ElfBin(std::string_view elf_filename) noexcept(false); 40 | ~ElfBin(); 41 | 42 | // Don't allow copy. 43 | ElfBin(const Elf &rhs) = delete; 44 | ElfBin &operator=(const Elf &rhs) = delete; 45 | 46 | // Creates an ElfSymbol object to iterate through elf symbols and 47 | // manipulate them. 48 | ElfSymbol Symbols() 49 | { 50 | return ElfSymbol{ elf_ }; 51 | } 52 | 53 | // Creates an ElfRela object to iterate through elf rela sections and 54 | // manipulate them. 55 | ElfRela Relas() 56 | { 57 | return ElfRela{ elf_ }; 58 | } 59 | 60 | void UpdateSection(size_t sec_idx, void *data, size_t size) 61 | noexcept(false); 62 | 63 | // Gets section data w/ given section index. 64 | std::unique_ptr > GetSection(size_t sec_idx); 65 | 66 | std::string_view SectionName(size_t sec_idx); 67 | 68 | // Locates the section, .modinfo, and returns module name 69 | // The section consists of key=value pair seperated by '\0' 70 | // 71 | // Contents of section .modinfo: 72 | // 0000 6c697665 70617463 683d5900 6c696365 livepatch=Y.lice 73 | // 0010 6e73653d 47504c00 64657065 6e64733d nse=GPL.depends= 74 | // 0020 00726574 706f6c69 6e653d59 006e616d .retpoline=Y.nam 75 | // 0030 653d6b65 726e656c 5f6c6976 65706174 e=kernel_livepat 76 | // 0040 63680076 65726d61 6769633d 342e3135 ch.vermagic=4.15 77 | // 0050 2e302d73 6d702d44 45562053 4d50206d .0-smp-DEV SMP m 78 | // 0060 6f645f75 6e6c6f61 64206d6f 64766572 od_unload modver 79 | // 0070 73696f6e 732000 sions . 80 | std::string ModName(); 81 | 82 | // Gets string section index for section names. 83 | size_t GetStringSectionIndex(); 84 | 85 | // Creates a new relocation section for livepatched symbols. This is 86 | // "non-standard" relocation section for kernel livepatch subsystem. section_id 87 | // points to text section that requires relocation. section_name is offset in 88 | // section name string section. Relocation infomation is stored in rela_vector for 89 | // creating rela section. 90 | void CreateKlpRela(size_t section_id, size_t symtab_id, 91 | size_t section_name, 92 | std::vector *rela_vector) 93 | noexcept(false); 94 | 95 | void UpdateRela(size_t section_id, 96 | std::vector *rela_vector) 97 | noexcept(false); 98 | 99 | // Write down changes to the elf binary. This should be called 100 | // before this object goes away. 101 | void ElfUpdate() noexcept(false); 102 | 103 | private: 104 | Elf_Data *GetElfSectionData(size_t sec_idx) noexcept(false); 105 | 106 | int elf_fd_ = -1; 107 | Elf *elf_ = nullptr; 108 | }; 109 | 110 | #endif // ELF_BIN_H_ 111 | -------------------------------------------------------------------------------- /elf_error.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #include "elf_error.h" 19 | 20 | #include 21 | 22 | #include 23 | 24 | namespace 25 | { 26 | struct ElfErrorCategory : std::error_category { 27 | const char *name() const noexcept override 28 | { 29 | return "elf"; 30 | } 31 | std::string message(int ev) const override; 32 | }; 33 | 34 | std::string ElfErrorCategory::message(int ev) const 35 | { 36 | if (ev < static_cast(ElfErrorCode::CUSTOM_ERROR)) 37 | return elf_errmsg(ev); 38 | 39 | switch (static_cast(ev)) { 40 | case ElfErrorCode::NO_SYMTAB: 41 | return "no symbol table found"; 42 | case ElfErrorCode::INVALID_KLP_PREFIX: 43 | return "invalid livepatch prefix"; 44 | case ElfErrorCode::INVALID_ELF_SYMBOL: 45 | return "invalid ELF symbol"; 46 | case ElfErrorCode::NO_RELA_SECTION: 47 | return "no rela section in an ELF file"; 48 | case ElfErrorCode::RELA_SECTION_NOT_FOUND: 49 | return "(given) rela section cannot be found"; 50 | case ElfErrorCode::SAME_SYMBOL_FILENAME: 51 | return "ELF contains same symbol && filename combination"; 52 | default: 53 | return "unrecognized error"; 54 | } 55 | } 56 | 57 | const ElfErrorCategory _ElfErrorCategory{}; 58 | } // namespace 59 | 60 | std::error_code make_error_code(ElfErrorCode e) 61 | { 62 | return { static_cast(e), _ElfErrorCategory }; 63 | } 64 | -------------------------------------------------------------------------------- /elf_error.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #ifndef ELF_ERROR_H_ 19 | #define ELF_ERROR_H_ 20 | 21 | #include 22 | 23 | // This enum class is used to create std::error_code for elf handling. 24 | // Most of elf error codes come from elf_errno() while their error messages 25 | // are generated by elf_errmsg(). So, there are few error codes defined 26 | // here. 1~0xFFF are reserved for elf_errno(). From 0x1000, custom error 27 | // codes are defined. 28 | enum class ElfErrorCode : int { 29 | NO_ERROR = 0, 30 | CUSTOM_ERROR = 0x1000, 31 | NO_SYMTAB, 32 | INVALID_KLP_PREFIX, 33 | INVALID_ELF_SYMBOL, 34 | NO_RELA_SECTION, 35 | RELA_SECTION_NOT_FOUND, 36 | SAME_SYMBOL_FILENAME, 37 | }; 38 | 39 | namespace std 40 | { 41 | template <> struct is_error_code_enum : true_type { 42 | }; 43 | } // namespace std 44 | 45 | std::error_code make_error_code(ElfErrorCode e); 46 | 47 | #endif // ELF_ERROR_H_ 48 | -------------------------------------------------------------------------------- /elf_rela.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #include "elf_rela.h" 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "elf_error.h" 25 | #include "llvm/Support/raw_ostream.h" 26 | 27 | namespace 28 | { 29 | void throw_gelf_error() 30 | { 31 | throw std::error_code{ static_cast(elf_errno()) }; 32 | } 33 | 34 | bool HasAllocFlag(Elf *elf, size_t sec_id) 35 | { 36 | Elf_Scn *scn = elf_getscn(elf, sec_id); 37 | if (scn == nullptr) { 38 | throw_gelf_error(); 39 | } 40 | 41 | GElf_Shdr sec_header{}; 42 | if (!gelf_getshdr(scn, &sec_header)) { 43 | throw_gelf_error(); 44 | } 45 | 46 | return sec_header.sh_flags & SHF_ALLOC; 47 | } 48 | } // namespace 49 | 50 | ElfRela::ElfRela(Elf *elf) noexcept(false) : elf_(elf), symbol_(elf_) 51 | { 52 | if (!GetNextRela()) { 53 | throw std::error_code{ ElfErrorCode::NO_RELA_SECTION }; 54 | } 55 | } 56 | 57 | ElfRela::RelaEntry *ElfRela::Entry() noexcept(false) 58 | { 59 | if (gelf_getrela(rela_data_, rela_cursor_, &rela_entry_) == nullptr) { 60 | throw_gelf_error(); 61 | } 62 | return &rela_entry_; 63 | } 64 | 65 | std::string_view ElfRela::Name() 66 | { 67 | return symbol_.Name(GELF_R_SYM(Entry()->r_info)); 68 | } 69 | 70 | void ElfRela::PrintCurrentEntry() 71 | { 72 | llvm::outs() 73 | << "Section: " << SectionId() << ", Symbol: " << Name() << "\n"; 74 | } 75 | 76 | bool ElfRela::HasSectionIndex(ElfSymbol::SectionIndex idx) 77 | { 78 | return symbol_.HasSectionIndex(idx, GELF_R_SYM(Entry()->r_info)); 79 | } 80 | 81 | void ElfRela::SetSectionIndex(ElfSymbol::SectionIndex idx) 82 | { 83 | return symbol_.SetSectionIndex(idx, GELF_R_SYM(Entry()->r_info)); 84 | } 85 | 86 | Elf_Scn *ElfRela::GetNextRela() 87 | { 88 | while ((scn_ = elf_nextscn(elf_, scn_))) { 89 | gelf_getshdr(scn_, &rela_header_); 90 | // Section relocated by KLP RELA should have SHF_ALLOC flag because 91 | // kernel module loader frees sections without the flag before KLP 92 | // RELA kicks in. So, skip RELA sections for sections without the 93 | // flag. 94 | if (rela_header_.sh_type == SHT_RELA && 95 | HasAllocFlag(elf_, this->SectionId())) 96 | break; 97 | } 98 | if (scn_ == nullptr) { 99 | rela_cursor_ = std::numeric_limits::max(); 100 | rela_data_ = nullptr; 101 | rela_count_ = 0; 102 | return nullptr; 103 | } 104 | 105 | rela_data_ = elf_getdata(scn_, nullptr); 106 | rela_count_ = rela_header_.sh_size / rela_header_.sh_entsize; 107 | if (rela_count_ > 0) { 108 | // rela_cursor_ starts with 0 while terminating condition for next 109 | // iteration is rela_cursor_ == rela_count_. 110 | rela_count_--; 111 | } 112 | rela_cursor_ = 0; 113 | 114 | return scn_; 115 | } 116 | 117 | bool ElfRela::Iterator::operator==(const Iterator &other) const 118 | { 119 | if (rela_ != other.rela_) { 120 | return false; 121 | } 122 | 123 | if (rela_ == nullptr) { 124 | return true; 125 | } 126 | 127 | return rela_->elf_ == other.rela_->elf_; 128 | } 129 | 130 | ElfRela::Iterator &ElfRela::Iterator::operator++() 131 | { 132 | if (rela_ == nullptr) { 133 | return *this; 134 | } 135 | 136 | if (rela_->rela_cursor_ >= rela_->rela_count_) { 137 | if (!rela_->GetNextRela()) { 138 | rela_ = nullptr; 139 | } 140 | return *this; 141 | } 142 | rela_->rela_cursor_++; 143 | return *this; 144 | } 145 | -------------------------------------------------------------------------------- /elf_rela.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #ifndef ELF_RELA_H_ 19 | #define ELF_RELA_H_ 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "elf_symbol.h" 33 | 34 | // This class creates an iterator to browse through all rela sections in 35 | // elf binary where corresponding sections for the rela have 'ALLOC' flag. 36 | // This class also provides simple API to handl the rela sections. 37 | class ElfRela final { 38 | public: 39 | using RelaEntry = GElf_Rela; 40 | using RelaEntryMap = 41 | std::unordered_map >; 42 | using KlpRelaEntryMap = std::map< 43 | std::pair, 44 | std::vector >; 45 | 46 | ElfRela(Elf *elf) noexcept(false); 47 | ~ElfRela() = default; 48 | 49 | // Don't allow copy. 50 | ElfRela(const ElfRela &rhs) = delete; 51 | ElfRela &operator=(const ElfRela &rhs) = delete; 52 | 53 | // Iterator to browse through all rela section. This iterator does not 54 | // support backward move. This is mainly used for ranged-for. 55 | class Iterator final 56 | : public std::iterator { 57 | public: 58 | Iterator(ElfRela *rela) : rela_(rela) 59 | { 60 | } 61 | ~Iterator() = default; 62 | 63 | bool operator==(const Iterator &other) const; 64 | bool operator!=(const Iterator &other) const 65 | { 66 | return !operator==(other); 67 | } 68 | ElfRela *operator*() const 69 | { 70 | return rela_; 71 | } 72 | Iterator &operator++(); 73 | 74 | private: 75 | ElfRela *rela_ = nullptr; 76 | }; 77 | 78 | Iterator begin() 79 | { 80 | return Iterator(this); 81 | } 82 | Iterator end() 83 | { 84 | return Iterator(nullptr); 85 | } 86 | 87 | // Return current relocation entry that the current iterator points to. 88 | RelaEntry *Entry() noexcept(false); 89 | // Returns symbol name for current relocation entry. 90 | std::string_view Name() noexcept(false); 91 | // Returns section id that corresponds to the current relocation section. 92 | size_t SectionId() 93 | { 94 | return rela_header_.sh_info; 95 | } 96 | size_t SymTabId() 97 | { 98 | return rela_header_.sh_link; 99 | } 100 | bool HasSectionIndex(ElfSymbol::SectionIndex idx); 101 | void SetSectionIndex(ElfSymbol::SectionIndex idx); 102 | void PrintCurrentEntry(); 103 | 104 | private: 105 | Elf_Scn *GetNextRela(); 106 | 107 | Elf *elf_ = nullptr; 108 | Elf_Scn *scn_ = nullptr; 109 | GElf_Shdr rela_header_ = {}; 110 | Elf_Data *rela_data_ = nullptr; 111 | RelaEntry rela_entry_ = {}; 112 | size_t rela_cursor_ = std::numeric_limits::max(); 113 | size_t rela_count_ = 0; 114 | 115 | ElfSymbol symbol_; 116 | }; 117 | 118 | #endif // ELF_RELA_H_ 119 | -------------------------------------------------------------------------------- /elf_symbol.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #include "elf_symbol.h" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include "elf_error.h" 31 | #include "llvm/ADT/Twine.h" 32 | #include "llvm/IR/Function.h" 33 | #include "llvm/IR/Module.h" 34 | #include "llvm/Support/raw_ostream.h" 35 | 36 | using namespace llvm; 37 | 38 | namespace 39 | { 40 | StringRef kKLPLocalSym("klp.local.sym"); 41 | StringRef kLLpatchSym("__llpatch_symbol_"); 42 | 43 | void throw_gelf_error() 44 | { 45 | throw std::error_code{ static_cast(elf_errno()) }; 46 | } 47 | } // namespace 48 | 49 | ElfSymbol::ElfSymbol(Elf *elf) : elf_(elf) 50 | { 51 | Elf_Scn *scn = nullptr; 52 | GElf_Shdr sym_sec_hdr; 53 | while ((scn = elf_nextscn(elf_, scn))) { 54 | gelf_getshdr(scn, &sym_sec_hdr); 55 | if (sym_sec_hdr.sh_type == SHT_SYMTAB) 56 | break; 57 | } 58 | if (!scn) { 59 | throw std::error_code{ ElfErrorCode::NO_SYMTAB }; 60 | } 61 | 62 | str_sec_idx_ = sym_sec_hdr.sh_link; 63 | symtab_ = elf_getdata(scn, nullptr); 64 | sym_count_ = sym_sec_hdr.sh_size / sym_sec_hdr.sh_entsize; 65 | if (sym_count_ > 0) { 66 | // sym_cursor_ starts with 0 while terminating condition for next 67 | // iteration is sym_cursor_ == sym_count_. 68 | sym_count_--; 69 | } 70 | } 71 | 72 | ElfSymbol::Iterator ElfSymbol::begin() 73 | { 74 | return Iterator(this); 75 | } 76 | 77 | ElfSymbol::Iterator ElfSymbol::end() 78 | { 79 | return Iterator(nullptr); 80 | } 81 | 82 | std::string_view ElfSymbol::Name() const noexcept(false) 83 | { 84 | return Name(sym_cursor_); 85 | } 86 | 87 | std::string_view ElfSymbol::Name(size_t cursor) const noexcept(false) 88 | { 89 | GElf_Sym sym; 90 | GetGElfSymbol(&sym, cursor); 91 | return elf_strptr(elf_, str_sec_idx_, sym.st_name); 92 | } 93 | 94 | void ElfSymbol::Rename(uint32_t name_offset) noexcept(false) 95 | { 96 | GElf_Sym sym; 97 | GetGElfSymbol(&sym); 98 | sym.st_name = name_offset; 99 | SetGElfSymbol(&sym); 100 | } 101 | 102 | ElfSymbol::SymbolType ElfSymbol::Type() noexcept(false) 103 | { 104 | return Type(sym_cursor_); 105 | } 106 | ElfSymbol::SymbolType ElfSymbol::Type(size_t cursor) noexcept(false) 107 | { 108 | GElf_Sym sym; 109 | GetGElfSymbol(&sym, cursor); 110 | 111 | return static_cast(GELF_ST_TYPE(sym.st_info)); 112 | } 113 | 114 | size_t ElfSymbol::GetStringSectionIndex() 115 | { 116 | return str_sec_idx_; 117 | } 118 | 119 | bool ElfSymbol::HasSectionIndex(ElfSymbol::SectionIndex idx) 120 | { 121 | return HasSectionIndex(idx, sym_cursor_); 122 | } 123 | 124 | bool ElfSymbol::HasSectionIndex(ElfSymbol::SectionIndex idx, size_t cursor) 125 | { 126 | GElf_Sym sym; 127 | GetGElfSymbol(&sym, cursor); 128 | return sym.st_shndx == static_cast(idx); 129 | } 130 | 131 | void ElfSymbol::SetSectionIndex(ElfSymbol::SectionIndex idx) 132 | { 133 | SetSectionIndex(idx, sym_cursor_); 134 | } 135 | 136 | void ElfSymbol::SetSectionIndex(ElfSymbol::SectionIndex idx, size_t cursor) 137 | { 138 | GElf_Sym sym; 139 | GetGElfSymbol(&sym, cursor); 140 | sym.st_shndx = static_cast(idx); 141 | SetGElfSymbol(&sym, cursor); 142 | } 143 | 144 | void ElfSymbol::GetGElfSymbol(GElf_Sym *sym) const noexcept(false) 145 | { 146 | GetGElfSymbol(sym, sym_cursor_); 147 | } 148 | 149 | void ElfSymbol::GetGElfSymbol(GElf_Sym *sym, size_t cursor) const 150 | noexcept(false) 151 | { 152 | if (cursor == std::numeric_limits::max()) { 153 | errs() << "sym cursor, " << cursor << ", is invalid\n"; 154 | throw std::error_code{ ElfErrorCode::INVALID_ELF_SYMBOL }; 155 | } 156 | if (gelf_getsym(symtab_, cursor, sym) == nullptr) { 157 | throw_gelf_error(); 158 | } 159 | } 160 | 161 | void ElfSymbol::SetGElfSymbol(GElf_Sym *sym) noexcept(false) 162 | { 163 | SetGElfSymbol(sym, sym_cursor_); 164 | } 165 | 166 | void ElfSymbol::SetGElfSymbol(GElf_Sym *sym, size_t cursor) noexcept(false) 167 | { 168 | if (cursor == std::numeric_limits::max()) { 169 | errs() << "sym cursor, " << cursor << ", is invalid\n"; 170 | throw std::error_code{ ElfErrorCode::INVALID_ELF_SYMBOL }; 171 | } 172 | gelf_update_sym(symtab_, cursor, sym); 173 | } 174 | 175 | bool ElfSymbol::IsKLPLocalSymbol() const noexcept(false) 176 | { 177 | return StringRef(Name()).startswith(Twine(kKLPLocalSym, ":").str()); 178 | } 179 | 180 | bool ElfSymbol::IsLLpatchSymbol() const noexcept(false) 181 | { 182 | return StringRef(Name()).startswith(kLLpatchSym); 183 | } 184 | 185 | std::string_view ElfSymbol::GetLLpatchSymbolAlias() const noexcept(false) 186 | { 187 | if (!IsLLpatchSymbol()) { 188 | return ""; 189 | } 190 | return Name().substr(kLLpatchSym.size(), std::string::npos); 191 | } 192 | 193 | std::string ElfSymbol::CreateKlpLocalSymName(StringRef sym_name) 194 | { 195 | return (Twine(kKLPLocalSym, ":") + Twine(sym_name)).str(); 196 | } 197 | 198 | namespace 199 | { 200 | std::string RemoveBasePath(StringRef path, StringRef base_path) 201 | { 202 | return path.split(base_path).second.ltrim("./").str(); 203 | } 204 | } // namespace 205 | 206 | std::string ElfSymbol::CreateLivepatchedFunctionName(const Function &fn, 207 | StringRef base_path) 208 | { 209 | return fn.getName().str() + ":" + 210 | RemoveBasePath(fn.getParent()->getSourceFileName(), base_path); 211 | } 212 | 213 | // CreateLivepatchedSymbolName - Create a unique name for the global. The 214 | // format we're using is: 215 | // 216 | // klp.local.sym:orig_name:source_filename 217 | // ^ ^ ^ ^ ^ ^ 218 | // |___________| |_______| |_____________| 219 | // [A] [B] [C] 220 | // 221 | // [A]: Prefix. 222 | // [B]: The original symbol name. 223 | // [C]: The source filename, to help with disambiguation. 224 | std::string ElfSymbol::CreateLivepatchedSymbolName(StringRef orig_name, 225 | StringRef filename, 226 | StringRef base_path) 227 | { 228 | return ElfSymbol::CreateKlpLocalSymName(orig_name) + ":" + 229 | RemoveBasePath(filename, base_path); 230 | } 231 | 232 | ElfSymbol::Iterator::Iterator(ElfSymbol *symbol) noexcept(false) 233 | : symbol_(symbol) 234 | { 235 | if (symbol_ == nullptr) { 236 | return; 237 | } 238 | // First symbol, sym_cursor = 0, is always a dummy symbol w/ a null 239 | // name. So, skip it. 240 | symbol_->sym_cursor_ = 1; 241 | } 242 | 243 | bool ElfSymbol::Iterator::operator==(const Iterator &other) const 244 | { 245 | if (symbol_ != other.symbol_) { 246 | return false; 247 | } 248 | 249 | if (symbol_ == nullptr) { 250 | return true; 251 | } 252 | 253 | return symbol_->elf_ == other.symbol_->elf_; 254 | } 255 | 256 | bool ElfSymbol::Iterator::operator!=(const Iterator &other) const 257 | { 258 | return !operator==(other); 259 | } 260 | 261 | ElfSymbol *ElfSymbol::Iterator::operator*() const 262 | { 263 | return symbol_; 264 | } 265 | 266 | ElfSymbol::Iterator &ElfSymbol::Iterator::operator++() 267 | { 268 | if (symbol_ == nullptr) { 269 | return *this; 270 | } 271 | 272 | if (symbol_->sym_cursor_ >= symbol_->sym_count_) { 273 | symbol_->sym_cursor_ = std::numeric_limits::max(); 274 | symbol_ = nullptr; 275 | return *this; 276 | } 277 | symbol_->sym_cursor_++; 278 | return *this; 279 | } 280 | -------------------------------------------------------------------------------- /elf_symbol.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #ifndef ELF_SYMBOL_H_ 19 | #define ELF_SYMBOL_H_ 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "llvm/ADT/StringRef.h" 29 | 30 | namespace llvm 31 | { 32 | class Function; 33 | } 34 | 35 | // This class creates an iterator to browse through all symbols in elf 36 | // binary and handles the symbols in it. 37 | class ElfSymbol final { 38 | public: 39 | enum class SectionIndex : std::uint16_t { 40 | UNDEF = 0, /* Undefined section */ 41 | LORESERVE = 0xff00, /* Start of reserved indices */ 42 | LOPROC = 0xff00, /* Start of processor-specific */ 43 | HIPROC = 0xff1f, /* End of processor-specific */ 44 | LIVEPATCH = 0xff20, /* Special for kernel livepatch */ 45 | HIOS = 0xff3f, /* End of OS-specific */ 46 | ABS = 0xfff1, /* Associated symbol is absolute */ 47 | COMMON = 0xfff2, /* Associated symbol is common */ 48 | XINDEX = 0xffff, /* Index is in extra table. */ 49 | HIRESERVE = 0xffff /* End of reserved indices */ 50 | }; 51 | 52 | // Symbol types. enum values, STT_*, come from gelf.h 53 | enum class SymbolType : std::uint8_t { 54 | NOTYPE = STT_NOTYPE, 55 | OBJECT = STT_OBJECT, 56 | FUNC = STT_FUNC, 57 | SECTION = STT_SECTION, 58 | FILE = STT_FILE, 59 | COMMON = STT_COMMON, 60 | TLS = STT_TLS, 61 | NUM = STT_NUM, 62 | LOOS = STT_LOOS, 63 | HIOS = STT_HIOS, 64 | LOPROC = STT_LOPROC, 65 | HIPROC = STT_HIPROC 66 | }; 67 | 68 | ElfSymbol(Elf *elf) noexcept(false); 69 | ~ElfSymbol() = default; 70 | 71 | // Don't allow copy. 72 | ElfSymbol(const ElfSymbol &rhs) = delete; 73 | ElfSymbol &operator=(const ElfSymbol &rhs) = delete; 74 | 75 | // Iterator to browse through all symbols. This iterator does not 76 | // support backward move. This is mainly used for ranged-for. 77 | class Iterator final 78 | : public std::iterator { 79 | public: 80 | Iterator(ElfSymbol *symbol); 81 | ~Iterator() = default; 82 | 83 | bool operator==(const Iterator &other) const; 84 | bool operator!=(const Iterator &other) const; 85 | ElfSymbol *operator*() const; 86 | Iterator &operator++(); 87 | 88 | private: 89 | ElfSymbol *symbol_ = nullptr; 90 | }; 91 | 92 | Iterator begin(); 93 | Iterator end(); 94 | 95 | std::string_view Name() const noexcept(false); 96 | std::string_view Name(size_t cursor) const noexcept(false); 97 | 98 | // Symbol entry in a symbol table has an offset in string section 99 | // for symbols' name. Unfortunately, gelf doesn't provide a good 100 | // abstraction for renaming symbol, which requires a clear 101 | // understanding on the structure of ELF binary. For now, This 102 | // function takes a new offset for symbol name and simply updates 103 | // its index. Therefore, outside of this class, string section 104 | // should be modified, which is ugly. 105 | void Rename(uint32_t name_offset) noexcept(false); 106 | 107 | SymbolType Type() noexcept(false); 108 | SymbolType Type(size_t cursor) noexcept(false); 109 | 110 | size_t GetStringSectionIndex(); 111 | 112 | bool HasSectionIndex(SectionIndex idx); 113 | bool HasSectionIndex(SectionIndex idx, size_t cursor); 114 | void SetSectionIndex(SectionIndex idx); 115 | void SetSectionIndex(SectionIndex idx, size_t cursor); 116 | 117 | bool IsKLPLocalSymbol() const noexcept(false); 118 | bool IsLLpatchSymbol() const noexcept(false); 119 | std::string_view GetLLpatchSymbolAlias() const; 120 | 121 | static std::string CreateKlpLocalSymName(llvm::StringRef sym_name); 122 | static std::string 123 | CreateLivepatchedFunctionName(const llvm::Function &fn, 124 | llvm::StringRef base_path); 125 | static std::string 126 | CreateLivepatchedSymbolName(llvm::StringRef orig_name, 127 | llvm::StringRef filename, 128 | llvm::StringRef base_path); 129 | 130 | private: 131 | void GetGElfSymbol(GElf_Sym *sym) const noexcept(false); 132 | void GetGElfSymbol(GElf_Sym *sym, size_t cursor) const noexcept(false); 133 | void SetGElfSymbol(GElf_Sym *sym) noexcept(false); 134 | void SetGElfSymbol(GElf_Sym *sym, size_t cursor) noexcept(false); 135 | 136 | Elf *elf_ = nullptr; 137 | size_t str_sec_idx_ = 0; 138 | Elf_Data *symtab_ = nullptr; 139 | size_t sym_cursor_ = std::numeric_limits::max(); 140 | size_t sym_count_ = 0; 141 | }; 142 | 143 | #endif // ELF_SYMBOL_H_ 144 | -------------------------------------------------------------------------------- /examples/llpatch-callback-shadow-var.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | /* 19 | * This file implements example code to show 20 | * 21 | * 1) how to implement callbacks for kernel livepatch, 22 | * 23 | * 2) how to access global variables defined in kernel module or vmlinux. 24 | * 25 | * For more details, refer to ${llpatch}/templates/llpatch.h and 26 | * ${llpatch}/templates/llpatch-callbacks.c 27 | */ 28 | #include 29 | #include 30 | #include 31 | 32 | #include "llpatch.h" 33 | 34 | typedef char c_array_t[PAGE_SIZE]; 35 | 36 | // c_array_t: type of global variable defined in kernel module 37 | // apple_fruit: alias to the variable 38 | // kernel/livepatch/test/test-attr-apple.c: path to the file w/ the variable 39 | // fruit: name of variable in the file for the kernel module 40 | LLPATCH_DECLARE_SYMBOL(c_array_t, apple_fruit, 41 | kernel/livepatch/test/test-attr-apple.c, fruit); 42 | 43 | LLPATCH_DECLARE_SYMBOL( 44 | c_array_t, banana_fruit, 45 | kernel/livepatch/test/test-attr-banana.c, fruit); 46 | 47 | #define SHADOW_DATA_ID 1 48 | 49 | /* Executed on object patching (ie, patch enablement) */ 50 | static int pre_patch_callback(struct klp_object *obj) 51 | { 52 | char *shadow_apple, *shadow_banana; 53 | 54 | pr_info("\nAccessing llpatch symbol in %s\n", __func__); 55 | 56 | pr_info("apple_fruit is %s at %p\n", 57 | LLPATCH_SYMBOL(apple_fruit), 58 | LLPATCH_SYMBOL(apple_fruit)); 59 | pr_info("banana_fruit is %s at %p\n", 60 | LLPATCH_SYMBOL(banana_fruit), 61 | LLPATCH_SYMBOL(banana_fruit)); 62 | 63 | /* 64 | * @obj: pointer to parent object 65 | * @id: data identifier 66 | * @size: size of attached data 67 | * @gfp_flags: GFP mask for allocation 68 | * @ctor: custom constructor to initialize the shadow data (optional) 69 | * @ctor_data: pointer to any data needed by @ctor (optional) 70 | */ 71 | shadow_apple = klp_shadow_alloc(/*obj*/LLPATCH_SYMBOL(apple_fruit), 72 | /*id*/SHADOW_DATA_ID, 73 | /*size*/sizeof(c_array_t), 74 | /*gfp_flags*/GFP_KERNEL, 75 | /*ctor*/NULL, /*ctor_data*/NULL); 76 | strcpy(shadow_apple, "Pink Lady"); 77 | pr_info("shadow for apple is added\n"); 78 | 79 | shadow_banana = klp_shadow_alloc(/*obj*/LLPATCH_SYMBOL(banana_fruit), 80 | /*id*/SHADOW_DATA_ID, 81 | /*size*/sizeof(c_array_t), 82 | /*gfp_flags*/GFP_KERNEL, 83 | /*ctor*/NULL, /*ctor_data*/NULL); 84 | strcpy(shadow_banana, "Plantain"); 85 | pr_info("shadow for banana is added\n"); 86 | 87 | pr_info("Done in %s\n\n", __func__); 88 | 89 | return 0; 90 | } 91 | 92 | /* Executed after object patching */ 93 | static void post_patch_callback(struct klp_object *obj) 94 | { 95 | } 96 | 97 | /* Executed on object unpatching */ 98 | static void pre_unpatch_callback(struct klp_object *obj) 99 | { 100 | } 101 | 102 | /* Executed after object unpatching */ 103 | static void post_unpatch_callback(struct klp_object *obj) 104 | { 105 | pr_info("\nAccessing llpatch symbol in %s\n", __func__); 106 | 107 | pr_info("freeing shadow data for apple at %p\n", LLPATCH_SYMBOL(apple_fruit)); 108 | klp_shadow_free(LLPATCH_SYMBOL(apple_fruit), SHADOW_DATA_ID, NULL); 109 | 110 | pr_info("freeing shadow data for banana at %p\n", LLPATCH_SYMBOL(banana_fruit)); 111 | klp_shadow_free(LLPATCH_SYMBOL(banana_fruit), SHADOW_DATA_ID, NULL); 112 | 113 | pr_info("Done in %s\n\n", __func__); 114 | } 115 | -------------------------------------------------------------------------------- /fixup_command.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #include "fixup_command.h" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "elf_error.h" 37 | #include "elf_bin.h" 38 | #include "elf_rela.h" 39 | #include "elf_symbol.h" 40 | #include "symbol_map.h" 41 | #include "thin_archive.h" 42 | #include "llvm/Support/raw_ostream.h" 43 | 44 | using namespace llvm; 45 | namespace 46 | { 47 | struct FixupArgs { 48 | char *klp_patch_filename = nullptr; 49 | char *mod_filename = nullptr; 50 | char *symbol_map = nullptr; 51 | char *thin_archive = nullptr; 52 | bool create_klp_rela = false; 53 | bool quiet_mode = false; 54 | }; 55 | 56 | const char kFixupArgsDoc[] = ""; 57 | const char kFixupPrgDoc[] = "common fixup options:\n"; 58 | const struct argp_option kFixupOptions[] = { 59 | // name, key, arg, flags, doc, 60 | { "mod", 'm', "MOD", 0, 61 | "Path to kernel module. For vmlinux, don't specify" }, 62 | { "symbol_map", 's', "SYMBOL_MAP", 0, 63 | "Symbol map file for LLpatch symbols in livepatch wrapper" }, 64 | { "thin_archive", 't', "THIN_ARCHIVE", 0, 65 | "Thin archive file for kernel module or vmlinux" }, 66 | { "rela", 'r', nullptr, 0, "Create relocation section for KLP" }, 67 | { "quiet", 'q', nullptr, 0, "Don't print out any messages on fixup" }, 68 | { nullptr } 69 | }; 70 | 71 | constexpr std::string_view kKlpPrefix = ".klp.sym."; 72 | constexpr std::string_view kKlpRelaPrefix = ".klp.rela."; 73 | constexpr std::string_view kObjVmlinux = "vmlinux."; 74 | 75 | error_t ParseFixupOpt(int key, char *arg, struct argp_state *state) 76 | { 77 | FixupArgs *args = static_cast(state->input); 78 | 79 | switch (key) { 80 | case 'm': 81 | args->mod_filename = arg; 82 | break; 83 | case 'r': 84 | args->create_klp_rela = true; 85 | break; 86 | case 'q': 87 | args->quiet_mode = true; 88 | break; 89 | case 's': 90 | args->symbol_map = arg; 91 | break; 92 | case 't': 93 | args->thin_archive = arg; 94 | break; 95 | case ARGP_KEY_ARG: 96 | if (!args->klp_patch_filename) { 97 | args->klp_patch_filename = arg; 98 | } else { 99 | argp_usage(state); 100 | } 101 | break; 102 | case ARGP_KEY_END: 103 | if (!args->klp_patch_filename) { 104 | argp_usage(state); 105 | } 106 | break; 107 | default: 108 | return ARGP_ERR_UNKNOWN; 109 | } 110 | return 0; 111 | } 112 | 113 | } // namespace 114 | 115 | // Assumption: there is 1-to-1 correspondence between a relocation section 116 | // and the section that the relocation section relocates. The following is 117 | // the example. 118 | // 119 | // [Nr] Name Type Address Off Size ES Flg Lk Inf Al 120 | // [ 1] .text PROGBITS 0000000000000000 000040 0014ca 00 AX 0 0 16 121 | // [ 2] .rela.text RELA 0000000000000000 001510 002268 18 I 18 1 8 122 | // 123 | // ".rela.text" should be only one relocation section for ".text". 124 | std::error_code FixupCommand::CreateKlpRela(ElfBin *elf_bin) 125 | { 126 | ElfRela::KlpRelaEntryMap klp_rela_entry_map; 127 | ElfRela::RelaEntryMap rela_entry_map; 128 | std::unordered_map symtab_map; 129 | for (ElfRela *i : elf_bin->Relas()) { 130 | std::string sym_name(i->Name()); 131 | if (sym_name.find(kKlpPrefix) != 0) { 132 | // Store rela entry for non-livepatched symbols. 133 | rela_entry_map[i->SectionId()].emplace_back( 134 | *(i->Entry())); 135 | continue; 136 | } 137 | i->SetSectionIndex(ElfSymbol::SectionIndex::LIVEPATCH); 138 | 139 | size_t mod_name_start = kKlpPrefix.length(); 140 | size_t mod_name_end = 141 | sym_name.find('.', mod_name_start) - mod_name_start; 142 | const std::string &mod_name = 143 | sym_name.substr(mod_name_start, mod_name_end); 144 | 145 | if (!quiet_mode_) { 146 | out_ << "klp symbol[" << mod_name << "] :: "; 147 | i->PrintCurrentEntry(); 148 | } 149 | 150 | // Collect all relocation entries for livepatched symbols. 151 | klp_rela_entry_map[std::make_pair(mod_name, i->SectionId())] 152 | .emplace_back(*(i->Entry())); 153 | symtab_map[i->SectionId()] = i->SymTabId(); 154 | } 155 | 156 | // Update existing rela sections to avoid duplication with KLP rela 157 | // sections. 158 | for (auto &[section_id, rela_vector] : rela_entry_map) { 159 | // Update rela section that has livepatched symbols. 160 | elf_bin->UpdateRela(section_id, &rela_vector); 161 | } 162 | 163 | // Update RELA sections before adding new KLP RELA sections. 164 | elf_bin->ElfUpdate(); 165 | 166 | // Create new relocation section for KLP. 167 | std::unique_ptr > str_section = 168 | elf_bin->GetSection(elf_bin->GetStringSectionIndex()); 169 | 170 | for (auto &[entry_key, rela_vector] : klp_rela_entry_map) { 171 | elf_bin->CreateKlpRela(std::get(entry_key), 172 | symtab_map[std::get<1>(entry_key)], 173 | str_section->size(), &rela_vector); 174 | 175 | // Format for The name of a livepatch relocation section: 176 | // 177 | // .klp.rela.objname.section_name 178 | // ^ ^^ ^ ^ ^ 179 | // |________||_____| |__________| 180 | // [A] [B] [C] 181 | // [A]: prefix 182 | // [B]: vmlinux or module name that the symbol belongs. 183 | // [C]: section name to which this relocation section applies. should be "text" 184 | const std::string kKlpRelaName = 185 | std::string(kKlpRelaPrefix) + 186 | std::get(entry_key) + "." + 187 | std::string( 188 | elf_bin->SectionName(std::get<1>(entry_key))); 189 | 190 | llvm::outs() << "KLP rela section::" << kKlpRelaName << "\n"; 191 | std::copy(kKlpRelaName.begin(), kKlpRelaName.end(), 192 | std::back_insert_iterator >( 193 | *str_section)); 194 | str_section->push_back('\0'); 195 | } 196 | 197 | elf_bin->UpdateSection(elf_bin->GetStringSectionIndex(), 198 | str_section->data(), str_section->size()); 199 | 200 | elf_bin->ElfUpdate(); 201 | 202 | return Command::ErrorCode::NO_ERROR; 203 | } 204 | 205 | std::error_code FixupCommand::RenameKlpSymbols(ElfBin *elf_bin, 206 | std::string_view mod_filename, 207 | std::string_view symbol_map, 208 | std::string_view thin_archive) 209 | { 210 | // Load names for all "defined" symbols in kernel module if specified. 211 | std::unordered_set mod_symbol_set; 212 | std::string mod_name(kObjVmlinux); 213 | if (!mod_filename.empty()) { 214 | ElfBin mod_bin(mod_filename); 215 | for (ElfSymbol *i : mod_bin.Symbols()) { 216 | if (i->HasSectionIndex( 217 | ElfSymbol::SectionIndex::UNDEF)) { 218 | continue; 219 | } 220 | mod_symbol_set.insert(std::string(i->Name())); 221 | } 222 | mod_name = mod_bin.ModName() + "."; 223 | } 224 | 225 | // Elf binary always starts w/ dummy undefined symbol. iterator from 226 | // ElfSymbol skips the first dummy symbol. Hence, default memory buffer 227 | // for string section has '\0' by default. 228 | std::vector sym_name_buf{ '\0' }; 229 | ElfSymbol elf_symbols = elf_bin->Symbols(); 230 | size_t sym_name_offset = 0; 231 | 232 | auto RenameSymbol = [&sym_name_buf, &sym_name_offset]( 233 | ElfSymbol *i, 234 | const std::string_view new_name) { 235 | std::copy(new_name.begin(), new_name.end(), 236 | std::back_insert_iterator >( 237 | sym_name_buf)); 238 | sym_name_buf.push_back('\0'); 239 | i->Rename(sym_name_offset); 240 | sym_name_offset = sym_name_buf.size(); 241 | }; 242 | 243 | // This loop iterates through all symbols in the ELF binary and renames 244 | // symbol if it's undefined. While renaming the symbol, it also builds 245 | // up a memory buffer for symbol names. The buffer is used to update 246 | // string section in ELF binary after this loop. 247 | std::unique_ptr tar = 248 | ThinArchive::Create(std::string(thin_archive)); 249 | std::unique_ptr sym_map = 250 | SymbolMap::Create(std::string(symbol_map)); 251 | for (ElfSymbol *i : elf_symbols) { 252 | // __fentry__ is for kernel's ftrace. don't touch even though it's UND. 253 | if (!i->HasSectionIndex(ElfSymbol::SectionIndex::UNDEF) || 254 | i->Name() == "__fentry__") { 255 | RenameSymbol(i, i->Name()); 256 | continue; 257 | } 258 | 259 | StringRef RealSymName = i->Name(); 260 | StringRef SrcFile; 261 | std::string SymName = std::string(i->Name()); 262 | std::string RealSymNameStr = RealSymName.str(); 263 | 264 | if (sym_map) { 265 | if (i->IsLLpatchSymbol()) { 266 | auto alias = i->GetLLpatchSymbolAlias(); 267 | const auto &sym_entry = 268 | sym_map->QueryAlias(std::string(alias)); 269 | RealSymName = 270 | sym_entry[SymbolMap::ElemIndex::SYMBOL]; 271 | SrcFile = sym_entry[SymbolMap::ElemIndex::PATH]; 272 | mod_name = 273 | sym_entry[SymbolMap::ElemIndex::MOD_NAME] + 274 | "."; 275 | RealSymNameStr.assign(RealSymName); 276 | } else { 277 | // with symbol map given, only llpatch symbol should be KLP 278 | // symbol. 279 | RenameSymbol(i, RealSymNameStr); 280 | continue; 281 | } 282 | } else { 283 | if (i->IsKLPLocalSymbol()) { 284 | auto SplitName = RealSymName.split(':'); 285 | RealSymName = SplitName.second.split(':').first; 286 | SrcFile = SplitName.second.split(':').second; 287 | RealSymNameStr.assign(RealSymName); 288 | } 289 | 290 | if (mod_name != kObjVmlinux && 291 | mod_symbol_set.find(RealSymNameStr) == 292 | mod_symbol_set.end()) { 293 | // given kernel module doesn't have symbol name, which 294 | // implies EXPORTed symbol. So, do not mark this as 295 | // livepatched symbol. 296 | RenameSymbol(i, RealSymNameStr); 297 | continue; 298 | } 299 | } 300 | 301 | i->SetSectionIndex(ElfSymbol::SectionIndex::LIVEPATCH); 302 | 303 | // Rename the symbol for livepatching. The following is the format. 304 | // 305 | // .klp.sym.objname.symbol_name,sympos 306 | // ^ ^^ ^ ^ ^ ^ 307 | // |_______||_____| |_________| | 308 | // [A] [B] [C] [D] 309 | // 310 | // [A]: Prefix 311 | // [B]: vmlinux or module name that the symbol belongs. 312 | // [C]: Actual name of the symbol. 313 | // [D]: The position of the symbol in the object (as according 314 | // to kallsyms) This is used to differentiate 315 | // duplicate symbols within the same object. The 316 | // symbol position is expressed numerically (0, 1, 317 | // 2, ...). The symbol position of a unique symbol 318 | // is 0. 319 | int pos = 0; 320 | if (tar) { 321 | const auto &symbol = RealSymName.str(); 322 | const auto &filename = 323 | SrcFile.rsplit('.').first.str() + ".o"; 324 | pos = tar->QuerySymbol(symbol, filename); 325 | if (pos < 0) { 326 | errs() << "Symbol: " << symbol 327 | << ", Filename: " << filename << "\n" 328 | << "Fail to find the symbol in thin archive\n"; 329 | return Command::ErrorCode::SYM_FIND_FAILED; 330 | } 331 | } 332 | 333 | const std::string kKlpSymName = std::string(kKlpPrefix) + 334 | mod_name + RealSymNameStr + 335 | "," + std::to_string(pos); 336 | 337 | out_ << "KLP Symbols::" << RealSymNameStr << " --> " 338 | << kKlpSymName << "\n"; 339 | RenameSymbol(i, kKlpSymName); 340 | } 341 | 342 | // A new memory buffer for symbol name is built up in 343 | // sym_name_buf. need to replace old buffer w/ the new one for string 344 | // section before calling Elfbin::ElfUpdate() 345 | elf_bin->UpdateSection(elf_symbols.GetStringSectionIndex(), 346 | sym_name_buf.data(), sym_name_buf.size()); 347 | 348 | elf_bin->ElfUpdate(); 349 | 350 | return Command::ErrorCode::NO_ERROR; 351 | } 352 | 353 | std::unique_ptr FixupCommand::Create(int argc, char **argv) 354 | noexcept(false) 355 | { 356 | if (argc < 1) { 357 | throw std::error_code{ ErrorCode::NOT_ENOUGH_ARGS }; 358 | } 359 | 360 | FixupArgs arguments; 361 | struct argp argp = { kFixupOptions, ParseFixupOpt, kFixupArgsDoc, 362 | kFixupPrgDoc }; 363 | 364 | // First argument is a command, 'fixup' and it's already consumed. So, 365 | // argv[0] = argv[0] + argv[1] to let others used for options. 366 | std::string command = std::string(argv[0]) + " " + argv[1]; 367 | --argc; 368 | ++argv; 369 | argv[0] = const_cast(command.c_str()); 370 | argp_parse(&argp, argc, argv, /*flags=*/0, 371 | /*arg_index=*/nullptr, /*input=*/&arguments); 372 | 373 | auto *cmd = new FixupCommand(arguments.quiet_mode ? llvm::nulls() : 374 | llvm::outs()); 375 | 376 | cmd->quiet_mode_ = arguments.quiet_mode; 377 | cmd->klp_patch_filename_ = arguments.klp_patch_filename; 378 | if (arguments.mod_filename) { 379 | cmd->mod_filename_ = arguments.mod_filename; 380 | } 381 | cmd->create_klp_rela_ = arguments.create_klp_rela; 382 | 383 | if (arguments.thin_archive) { 384 | cmd->thin_archive_ = arguments.thin_archive; 385 | } 386 | 387 | if (arguments.symbol_map) { 388 | cmd->symbol_map_ = arguments.symbol_map; 389 | } 390 | 391 | return std::unique_ptr(cmd); 392 | } 393 | 394 | std::error_code FixupCommand::Run() 395 | { 396 | std::error_code ec; 397 | ElfBin elf_bin(klp_patch_filename_); 398 | if (create_klp_rela_) { 399 | ec = CreateKlpRela(&elf_bin); 400 | } else { 401 | ec = RenameKlpSymbols(&elf_bin, mod_filename_, symbol_map_, 402 | thin_archive_); 403 | } 404 | 405 | return ec; 406 | } 407 | -------------------------------------------------------------------------------- /fixup_command.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #ifndef FIXUP_COMMAND_H_ 19 | #define FIXUP_COMMAND_H_ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "command.h" 27 | #include "elf_bin.h" 28 | #include "llvm/Support/raw_ostream.h" 29 | 30 | // This class implements fixup command for kernel livepatch generation. The 31 | // 'fixup' command inputs an object file, klp_patch.o, that contains 32 | // implementation of livepatched functions. It iterates through symbol 33 | // table of the object file and identifies all UND (undefined) symbols. 34 | // Then, it renames symbol names based on the following rule. 35 | // https://www.kernel.org/doc/html/latest/livepatch/module-elf-format.html 36 | // It also creates a non-standard relocation section for kernel livepatch 37 | // subsystem. 38 | class FixupCommand : public Command { 39 | public: 40 | static constexpr std::string_view kCommandName = "fixup"; 41 | 42 | ~FixupCommand() override = default; 43 | 44 | // Don't allow copy. 45 | FixupCommand(const FixupCommand &rhs) = delete; 46 | FixupCommand &operator=(const FixupCommand &rhs) = delete; 47 | 48 | static std::unique_ptr Create(int argc, char **argv) 49 | noexcept(false); 50 | 51 | // Runs fixup command to rename UND symbols and create a relocation 52 | // section for kernel livepatch subsystem. 53 | std::error_code Run() override; 54 | 55 | private: 56 | FixupCommand(llvm::raw_ostream &out) : out_(out) 57 | { 58 | } 59 | 60 | std::error_code CreateKlpRela(ElfBin *elf_bin); 61 | std::error_code RenameKlpSymbols(ElfBin *elf_bin, 62 | std::string_view mod_filename, 63 | std::string_view symbol_map, 64 | std::string_view thin_archive); 65 | 66 | std::string klp_patch_filename_; 67 | // If changes for livepatch are made in kernel module, the path to the 68 | // module is required. For now, the fixup command assumes changes in 69 | // "single" kernel module. 70 | std::string mod_filename_; 71 | std::string symbol_map_; 72 | std::string thin_archive_; 73 | bool create_klp_rela_ = false; 74 | llvm::raw_ostream &out_; 75 | bool quiet_mode_ = false; 76 | }; 77 | 78 | #endif // FIXUP_COMMAND_H_ 79 | -------------------------------------------------------------------------------- /gen-symbol-map: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Copyright 2021 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # Author: yonghyun@google.com (Yonghyun Hwang) 18 | # 19 | # script to input .c file for livepatch callbacks, llpatch-callbacks.c and 20 | # output llpatch symbol map. 21 | 22 | #------------------------------------------------------------- 23 | # Shell setting 24 | #------------------------------------------------------------- 25 | set -E 26 | 27 | #------------------------------------------------------------- 28 | # Global variables 29 | #------------------------------------------------------------- 30 | declare -r G_GEN_SYMBOL_MAP_CMD="$0" 31 | declare -r G_GEN_SYMBOL_MAP_CMDLINE="$0 $@" 32 | declare -r G_GEN_SYMBOL_MAP_PATH=$(dirname $(readlink -f "${G_GEN_SYMBOL_MAP_CMD}")) 33 | declare -r G_TMP_DIR="$(mktemp -d -t livepatch.XXXXXXXXXX)" 34 | declare -r G_TMP_FILE="$(mktemp -t livepatch.XXXXXXXXXX)" 35 | 36 | declare G_KDIR="$(pwd)" 37 | declare G_LIVEPATCH_CALLBACK="" 38 | declare G_OUTPUT_FILE="" 39 | 40 | #------------------------------------------------------------- 41 | # Include library 42 | #------------------------------------------------------------- 43 | source "${G_GEN_SYMBOL_MAP_PATH}/libutil.bash" "gen-symbol-map" 44 | 45 | #------------------------------------------------------------- 46 | # Function definitions 47 | #------------------------------------------------------------- 48 | function cleanup() 49 | { 50 | local errCode="${1:-}" 51 | local lineNum="${2:-}" 52 | 53 | [[ ${errCode} == 0 ]] || \ 54 | util::log_error "trap at line: ${lineNum}, with error:${errCode}." 55 | 56 | rm -fr "${G_TMP_DIR}" "${G_TMP_FILE}" 57 | 58 | exit "${errCode}" 59 | } 60 | # enable exit trap for debugging purpose 61 | trap 'cleanup $? ${LINENO}' ERR INT TERM EXIT 62 | 63 | function print_usage() 64 | { 65 | cat <| "${G_TMP_FILE}" 173 | util::log_ok "Livepatch callback, $(basename ${LIVEPATCH_CALLBACK}), is parsed" 174 | 175 | >| "${G_OUTPUT_FILE}" 176 | util::log_info "Printing map info" 177 | local line="" 178 | while read -r line; do 179 | local -a tokens=($line) 180 | local path="${tokens[0]}" 181 | local gvar="${tokens[1]}" 182 | local ll_alias="${tokens[2]}" 183 | 184 | local obj_parent="" 185 | util::__find_obj_parent "${path%.c}.o" "obj_parent" 186 | local mod_name=$(modinfo -F 'name' $obj_parent) 187 | 188 | printf "\tModule: %s\tPath: %s\n\tGvar: %s\t\tAlias: %s\n" \ 189 | "${mod_name}" "${path}" "${gvar}" "${ll_alias}" 190 | echo "${mod_name}" "${path}" "${gvar}" "${ll_alias}" >> \ 191 | "${G_OUTPUT_FILE}" 192 | done < "${G_TMP_FILE}" 193 | util::log_ok "Output file, ${G_OUTPUT_FILE}, is generated" 194 | } 195 | 196 | #------------------------------------------------------------- 197 | # Main starts here 198 | #------------------------------------------------------------- 199 | parse_options "$@" 200 | 201 | validate_prepare 202 | 203 | generate_symbol_map "${G_LIVEPATCH_CALLBACK}" "${G_OUTPUT_FILE}" 204 | 205 | exit 0 206 | -------------------------------------------------------------------------------- /gen_command.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #include "gen_command.h" 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "elf_error.h" 31 | #include "elf_symbol.h" 32 | #include "thin_archive.h" 33 | #include "llvm/Support/raw_ostream.h" 34 | 35 | using namespace llvm; 36 | namespace fs = std::filesystem; 37 | namespace 38 | { 39 | struct GenArgs { 40 | char *klp_patch_filename = nullptr; 41 | char *output_directory = nullptr; 42 | char *kernel_directory = nullptr; 43 | char *mod_filename = nullptr; 44 | char *klp_mod_name = nullptr; 45 | char *thin_archive = nullptr; 46 | }; 47 | 48 | const char kGenArgsDoc[] = ""; 49 | const char kGenPrgDoc[] = "common gen options:\n"; 50 | const struct argp_option kGenOptions[] = { 51 | // name, key, arg, flags, doc, 52 | { "odir", 'o', "ODIR", 0, "Path to output dir" }, 53 | { "kdir", 'k', "KDIR", 0, "Path to kernel dir" }, 54 | { "mod", 'm', "MOD", 0, 55 | "Path to kernel module. for vmlinux, don't specify" }, 56 | { "name", 'n', "NAME", 0, "KLP module name" }, 57 | { "thin_archive", 't', "THIN_ARCHIVE", 0, 58 | "Thin archive file for kernel module or vmlinux" }, 59 | { nullptr } 60 | }; 61 | 62 | constexpr std::string_view kLivepatchPrefixElf = "__livepatch_"; 63 | constexpr std::string_view kLivepatchPrefixTmpl = "livepatch_"; 64 | constexpr std::string_view kTemplateExtension = ".tmpl"; 65 | constexpr std::string_view kPathToTemplate = "templates"; 66 | 67 | error_t ParseGenOpt(int key, char *arg, struct argp_state *state) 68 | { 69 | GenArgs *args = static_cast(state->input); 70 | 71 | switch (key) { 72 | case 'o': 73 | args->output_directory = arg; 74 | break; 75 | case 'k': 76 | args->kernel_directory = arg; 77 | break; 78 | case 'm': 79 | args->mod_filename = arg; 80 | break; 81 | case 'n': 82 | args->klp_mod_name = arg; 83 | break; 84 | case 't': 85 | args->thin_archive = arg; 86 | break; 87 | case ARGP_KEY_ARG: 88 | if (!args->klp_patch_filename) { 89 | args->klp_patch_filename = arg; 90 | } else { 91 | argp_usage(state); 92 | } 93 | break; 94 | case ARGP_KEY_END: 95 | if (!args->klp_patch_filename || !args->output_directory || 96 | !args->kernel_directory || !args->klp_mod_name) { 97 | argp_usage(state); 98 | } 99 | break; 100 | default: 101 | return ARGP_ERR_UNKNOWN; 102 | } 103 | return 0; 104 | } 105 | 106 | // Reads in_file and dumps its contents onto out_file till read line 107 | // contains the given marker. Once this hits the marker, it returns the 108 | // marked line. 109 | std::string DumpToMarker(std::fstream &in_file, std::fstream &out_file, 110 | std::string_view marker) 111 | { 112 | std::string line; 113 | bool marker_empty = marker.empty(); 114 | while (std::getline(in_file, line)) { 115 | if (!marker_empty && line.find(marker) != std::string::npos) { 116 | return line; 117 | } 118 | out_file << line << std::endl; 119 | } 120 | return ""; 121 | } 122 | 123 | // Opens two files, in and out, for given in_filename and 124 | // out_filename. Then, it returns tuple with in&out fstreams and error 125 | // code. 126 | std::tuple 127 | OpenInOutfiles(std::string_view in_filename, std::string_view out_filename) 128 | { 129 | std::fstream in_file(in_filename.data(), std::ios::in); 130 | std::fstream out_file(out_filename.data(), 131 | std::ios::out | std::ios::trunc); 132 | std::error_code ec; 133 | if (!in_file.is_open()) { 134 | errs() << "filename: " << in_filename << "\n"; 135 | ec = Command::ErrorCode::FILE_OPEN_FAILED; 136 | } else if (!out_file.is_open()) { 137 | errs() << "filename: " << out_filename << "\n"; 138 | ec = Command::ErrorCode::FILE_OPEN_FAILED; 139 | } 140 | 141 | return { std::move(in_file), std::move(out_file), ec }; 142 | } 143 | } // namespace 144 | 145 | GenCommand::GenCommand(int argc, char **argv) noexcept(false) 146 | { 147 | if (argc < 1) { 148 | throw std::error_code{ ErrorCode::NOT_ENOUGH_ARGS }; 149 | } 150 | 151 | GenArgs arguments; 152 | struct argp argp = { kGenOptions, ParseGenOpt, kGenArgsDoc, 153 | kGenPrgDoc }; 154 | 155 | // First argument is a command, 'gen' and it's already consumed. So, 156 | // argv[0] = argv[0] + argv[1] to let others used for options. 157 | std::string command = std::string(argv[0]) + " " + argv[1]; 158 | --argc; 159 | ++argv; 160 | argv[0] = const_cast(command.c_str()); 161 | argp_parse(&argp, argc, argv, /*flags=*/0, 162 | /*arg_index=*/nullptr, /*input=*/&arguments); 163 | 164 | klp_patch_filename_ = arguments.klp_patch_filename; 165 | output_directory_ = arguments.output_directory; 166 | kernel_directory_ = arguments.kernel_directory; 167 | if (arguments.mod_filename) { 168 | mod_filename_ = arguments.mod_filename; 169 | } 170 | klp_mod_name_ = arguments.klp_mod_name; 171 | if (arguments.thin_archive) { 172 | thin_archive_ = arguments.thin_archive; 173 | } 174 | 175 | static constexpr int buf_size = 4096; 176 | char livepatch_path[buf_size] = {}; 177 | if (readlink("/proc/self/exe", livepatch_path, buf_size) < 0) { 178 | throw std::error_code(errno, std::generic_category()); 179 | } 180 | livepatch_bin_directory_.assign(livepatch_path); 181 | 182 | // Remove name of executable binary from the path 183 | size_t file_pos = livepatch_bin_directory_.rfind("/"); 184 | livepatch_bin_directory_.erase( 185 | file_pos, livepatch_bin_directory_.length() - file_pos); 186 | } 187 | 188 | std::error_code GenCommand::Run() 189 | { 190 | // To generate the wrapper and linker script, names of livepatched 191 | // functions are required. Iterate through all symbols in ELF and get 192 | // the names. A name of livepatched function has a special prefix, 193 | // kLivepatchPrefixElf. 194 | std::vector > klp_func_names; 195 | ElfBin elf_bin(klp_patch_filename_); 196 | size_t prefix_len = kLivepatchPrefixElf.length(); 197 | for (ElfSymbol *i : elf_bin.Symbols()) { 198 | StringRef symbol = i->Name(); 199 | 200 | if (symbol.empty() || !symbol.startswith(kLivepatchPrefixElf)) { 201 | continue; 202 | } 203 | 204 | if (symbol.substr(1).find(kLivepatchPrefixElf) != 205 | std::string::npos) { 206 | // This means kLivepatchPrefixElf is matched in the middle of 207 | // string. This is not expected. error out. 208 | errs() << "symbol name: " << symbol << "\n"; 209 | return ElfErrorCode::INVALID_KLP_PREFIX; 210 | } 211 | 212 | auto [func_name, src_file] = 213 | symbol.drop_front(prefix_len).split(':'); 214 | klp_func_names.emplace_back( 215 | std::make_pair(func_name, src_file)); 216 | } 217 | 218 | if (klp_func_names.empty()) { 219 | errs() << "There are no livepatched functions.\n"; 220 | return std::error_code{ Command::ErrorCode::NOTHING_TO_PATCH }; 221 | } 222 | 223 | std::string mod_name = 224 | mod_filename_.empty() ? "" : ElfBin(mod_filename_).ModName(); 225 | std::error_code ec = GenerateWrapper(klp_func_names, mod_name); 226 | if (ec) { 227 | return ec; 228 | } 229 | 230 | ec = GenerateLdScript(klp_func_names); 231 | if (ec) { 232 | return ec; 233 | } 234 | 235 | ec = GenerateMakefile(); 236 | if (ec) { 237 | return ec; 238 | } 239 | 240 | ec = FixupKlpSymbols(&elf_bin); 241 | if (ec) { 242 | return ec; 243 | } 244 | 245 | return ErrorCode::NO_ERROR; 246 | } 247 | 248 | std::error_code GenCommand::GenerateWrapper( 249 | const std::vector > &klp_func_names, 250 | const std::string &mod_name) 251 | { 252 | static constexpr std::string_view kWrapperName = "livepatch.c"; 253 | static constexpr std::string_view kFuncMarker = 254 | "{{LIST_OF_LIVEPATCH_FUNCTIONS}}"; 255 | static constexpr std::string_view kStructMarker = 256 | "{{LIST_FOR_KLP_FUNC_STRUCT}}"; 257 | static constexpr std::string_view kObjMarker = "{{NAME_OF_OBJECT}}"; 258 | 259 | fs::path kTmplFilename = fs::path(livepatch_bin_directory_) / 260 | fs::path(kPathToTemplate) / 261 | fs::path(kWrapperName); 262 | kTmplFilename += fs::path(kTemplateExtension); 263 | 264 | const fs::path kOutFilename = 265 | fs::path(output_directory_) / fs::path(kWrapperName); 266 | auto [tmpl_file, out_file, ec] = 267 | OpenInOutfiles(kTmplFilename.c_str(), kOutFilename.c_str()); 268 | if (ec) { 269 | return ec; 270 | } 271 | 272 | DumpToMarker(tmpl_file, out_file, kFuncMarker); 273 | for (auto [func_name, src_file] : klp_func_names) { 274 | // void livepatch_${name_of_func}(void) 275 | out_file << "void " << std::string(kLivepatchPrefixTmpl) 276 | << func_name.str() << "(void);\n"; 277 | } 278 | 279 | std::unique_ptr tar = 280 | ThinArchive::Create(std::string(thin_archive_)); 281 | DumpToMarker(tmpl_file, out_file, kStructMarker); 282 | for (auto [func_name, src_file] : klp_func_names) { 283 | unsigned pos = 0; 284 | if (tar) { 285 | pos = tar->QuerySymbol( 286 | func_name.str(), 287 | src_file.rsplit('.').first.str() + ".o"); 288 | } 289 | 290 | //{ 291 | // .old_name = "${name_of_func}," 292 | // .new_func = "livepatch_${name_of_func}," 293 | // .old_sympos = ${old_symbol_position}, 294 | //}, 295 | out_file << "\t{\n" 296 | << "\t\t.old_name = \"" << func_name.str() << "\",\n" 297 | << "\t\t.new_func = " 298 | << std::string(kLivepatchPrefixTmpl) << func_name.str() 299 | << ",\n" 300 | << "\t\t.old_sympos = " << std::to_string(pos) << ",\n" 301 | << "\t},\n"; 302 | } 303 | 304 | DumpToMarker(tmpl_file, out_file, kObjMarker); 305 | 306 | // NULL means vmlinux. unless, it's module name 307 | // .name = NULL, 308 | // or 309 | // .name = "${mod_name}" 310 | out_file << "\t\t.name = " 311 | << (mod_name.empty() ? "NULL" : "\"" + mod_name + "\"") 312 | << ",\n"; 313 | 314 | // "" marker doesn't exist. hence dump to the end of file. 315 | DumpToMarker(tmpl_file, out_file, ""); 316 | return ErrorCode::NO_ERROR; 317 | } 318 | 319 | std::error_code GenCommand::GenerateLdScript( 320 | const std::vector > &klp_func_names) 321 | { 322 | static constexpr std::string_view kLdScriptName = "livepatch.lds"; 323 | 324 | fs::path kTmplFilename = fs::path(livepatch_bin_directory_) / 325 | fs::path(kPathToTemplate) / 326 | fs::path(kLdScriptName); 327 | kTmplFilename += fs::path(kTemplateExtension); 328 | 329 | const fs::path kOutFilename = 330 | fs::path(output_directory_) / fs::path(kLdScriptName); 331 | auto [tmpl_file, out_file, ec] = 332 | OpenInOutfiles(kTmplFilename.c_str(), kOutFilename.c_str()); 333 | if (ec) { 334 | return ec; 335 | } 336 | 337 | // "" marker doesn't exist. hence dump to the end of file. 338 | DumpToMarker(tmpl_file, out_file, ""); 339 | 340 | for (auto [func_name, src_file] : klp_func_names) { 341 | // ${func} = __${func} 342 | out_file << std::string(kLivepatchPrefixTmpl) 343 | << func_name.str() + " = " 344 | << std::string(kLivepatchPrefixElf) 345 | << func_name.str() + ";\n"; 346 | } 347 | 348 | return ErrorCode::NO_ERROR; 349 | } 350 | 351 | std::error_code GenCommand::GenerateMakefile() 352 | { 353 | static constexpr std::string_view kMakefileName = "Makefile"; 354 | static constexpr std::string_view kKernelPath = 355 | "{{PATH_TO_LINUX_KERNEL_SOURCE_TREE}}"; 356 | static constexpr std::string_view kKlpName = "{{NAME_OF_LIVEPATCH}}"; 357 | 358 | fs::path kTmplFilename = fs::path(livepatch_bin_directory_) / 359 | fs::path(kPathToTemplate) / 360 | fs::path(kMakefileName); 361 | kTmplFilename += fs::path(kTemplateExtension); 362 | 363 | const fs::path kOutFilename = 364 | fs::path(output_directory_) / fs::path(kMakefileName); 365 | auto [tmpl_file, out_file, ec] = 366 | OpenInOutfiles(kTmplFilename.c_str(), kOutFilename.c_str()); 367 | if (ec) { 368 | return ec; 369 | } 370 | 371 | std::string line = DumpToMarker(tmpl_file, out_file, kKernelPath); 372 | //KLP_BUILD = ... 373 | out_file << line.substr(0, line.find(kKernelPath)) + kernel_directory_ + 374 | "\n"; 375 | 376 | line = DumpToMarker(tmpl_file, out_file, kKlpName); 377 | //KLP_NAME = ... 378 | out_file << line.substr(0, line.find(kKlpName)) + klp_mod_name_ + "\n"; 379 | 380 | // "" marker doesn't exist. hence dump to the end of file. 381 | DumpToMarker(tmpl_file, out_file, ""); 382 | 383 | return ErrorCode::NO_ERROR; 384 | } 385 | 386 | std::error_code GenCommand::FixupKlpSymbols(ElfBin *elf_bin) 387 | { 388 | std::vector sym_name_buf{ '\0' }; 389 | ElfSymbol elf_symbols = elf_bin->Symbols(); 390 | 391 | for (ElfSymbol *symbol : elf_symbols) { 392 | size_t sym_name_offset = sym_name_buf.size(); 393 | StringRef sym_name = StringRef(symbol->Name()).split(':').first; 394 | 395 | std::copy(sym_name.begin(), sym_name.end(), 396 | std::back_insert_iterator >( 397 | sym_name_buf)); 398 | 399 | sym_name_buf.push_back('\0'); 400 | symbol->Rename(sym_name_offset); 401 | } 402 | 403 | elf_bin->UpdateSection(elf_symbols.GetStringSectionIndex(), 404 | sym_name_buf.data(), sym_name_buf.size()); 405 | elf_bin->ElfUpdate(); 406 | 407 | return ErrorCode::NO_ERROR; 408 | } 409 | -------------------------------------------------------------------------------- /gen_command.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #ifndef GEN_COMMAND_H_ 19 | #define GEN_COMMAND_H_ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "command.h" 27 | #include "elf_bin.h" 28 | 29 | #include "llvm/ADT/StringRef.h" 30 | 31 | // This class implements gen command for kernel livepatch generation. The 32 | // 'gen' command inputs an object file, klp_patch.o, that contains 33 | // implementation of livepatched functions. It iterates through symbol 34 | // table of the object file and indentifies all livepatched functions. 35 | // Then, it generates livepatch wrapper, livepatch.c, makefile, and linker 36 | // script out of template files. The wrapper is linked to the object file 37 | // to let kernel know what functions are available for the livepatch. The 38 | // linker script is used to resolve the address for livepatched functions 39 | // "declared" in the wrapper. 40 | class GenCommand : public Command { 41 | public: 42 | static constexpr std::string_view kCommandName = "gen"; 43 | 44 | GenCommand(int argc, char **argv) noexcept(false); 45 | ~GenCommand() override = default; 46 | 47 | // Don't allow copy. 48 | GenCommand(const GenCommand &rhs) = delete; 49 | GenCommand &operator=(const GenCommand &rhs) = delete; 50 | 51 | // Runs gen command and generates the wrapper, makefile, and linker 52 | // script. 53 | std::error_code Run() override; 54 | 55 | private: 56 | // Takes a vector of livepatched function names and generates a wrapper. 57 | std::error_code GenerateWrapper( 58 | const std::vector > 59 | &klp_func_names, 60 | const std::string &mod_name); 61 | // Takes a vector of livepatched function names and generates an ld script. 62 | std::error_code GenerateLdScript( 63 | const std::vector > 64 | &klp_func_names); 65 | std::error_code GenerateMakefile(); 66 | std::error_code FixupKlpSymbols(ElfBin *elf_bin); 67 | 68 | std::string klp_patch_filename_; 69 | std::string output_directory_; 70 | std::string kernel_directory_; 71 | std::string livepatch_bin_directory_; 72 | // If changes for livepatch are made in kernel module, the path to 73 | // the module is required. For now, the command assumes changes in 74 | // "single" kernel module. 75 | std::string mod_filename_; 76 | std::string klp_mod_name_; 77 | std::string thin_archive_; 78 | }; 79 | 80 | #endif // GEN_COMMAND_H_ 81 | -------------------------------------------------------------------------------- /libutil.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Copyright 2021 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # Author: yonghyun@google.com (Yonghyun Hwang) 18 | # 19 | # Library for shell script to generate a kernel livepatch 20 | 21 | #------------------------------------------------------------- 22 | # Shell setting 23 | #------------------------------------------------------------- 24 | set -E 25 | 26 | #------------------------------------------------------------- 27 | # Global variables 28 | #------------------------------------------------------------- 29 | declare -r G_KERNEL_VMLINUX="vmlinux" 30 | declare -r G_LOG_HEADER="${1}" 31 | declare -r G_MESG_COLOR_RED=$(tput setaf 1) 32 | declare -r G_MESG_COLOR_GREEN=$(tput setaf 2) 33 | declare -r G_MESG_COLOR_YELLOW=$(tput setaf 3) 34 | declare -r G_MESG_COLOR_NONE=$(tput sgr0) 35 | declare -r G_PREFIX_LIVEPATCH="llpatch" 36 | 37 | declare -r G_UTIL_SUFFIX_PATCH="patch" 38 | 39 | #------------------------------------------------------------- 40 | # Function definitions 41 | #------------------------------------------------------------- 42 | function util::log_ok() 43 | { 44 | echo -e "${G_MESG_COLOR_GREEN}${G_LOG_HEADER}[ OK |$(date +'%m/%d %H:%M')]::${G_MESG_COLOR_NONE} $@" 45 | return 0 46 | } 47 | 48 | function util::log_warn() 49 | { 50 | echo -e "${G_MESG_COLOR_YELLOW}${G_LOG_HEADER}[WARN|$(date +'%m/%d %H:%M')]::${G_MESG_COLOR_NONE} $@" >&2 51 | return 0 52 | } 53 | 54 | function util::log_info() 55 | { 56 | echo "${G_LOG_HEADER}[INFO|$(date +'%m/%d %H:%M')]:: $@" 57 | return 0 58 | } 59 | 60 | function util::log_error() 61 | { 62 | echo -e "${G_MESG_COLOR_RED}${G_LOG_HEADER}[ERR |$(date +'%m/%d %H:%M')]::${G_MESG_COLOR_NONE} $@" >&2 63 | return 0 64 | } 65 | 66 | function util::error() 67 | { 68 | util::log_error "$@" 69 | return 1 70 | } 71 | 72 | function util::get_livepatch_name() 73 | { 74 | local -r PATCH_NAME="${1}" 75 | local livepatch_name="$(basename "${PATCH_NAME}")" 76 | # sysfs is not happy with [-.] for the name of kernel modules. 77 | echo "${livepatch_name%.*}" | tr '\-.' '__' 78 | } 79 | 80 | # This funciton inputs a name of a patch file and generates a name of .ko file 81 | # for kernel livepatch. This is the rule from a comment in kpatch-build: 82 | # Only allow alphanumerics and '_' and '-' in the module name. Everything else 83 | # is replaced with '-'. Also truncate to 48 chars so the full name fits in the 84 | # kernel's 56-byte module name array. 85 | function util::get_livepatch_ko_name() 86 | { 87 | local -r PATCH_NAME="${1}" 88 | local ko_name="$(util::get_livepatch_name "${PATCH_NAME}")" 89 | ko_name="${G_PREFIX_LIVEPATCH}-${ko_name//[^a-zA-Z0-9_-]/-}" 90 | echo "${ko_name:0:56-${#G_PREFIX_LIVEPATCH}-1}.ko" 91 | } 92 | 93 | function util::get_livepatch_mod_name() 94 | { 95 | local -r PATCH_NAME="${1}" 96 | echo "${G_PREFIX_LIVEPATCH}_$(util::get_livepatch_name "${PATCH_NAME}")" 97 | } 98 | 99 | # this function parses map elements that is a single string. The string 100 | # consists of multiple elements seperated by seperator. 101 | function util::parse_map_elements() 102 | { 103 | local -r ELEM_SEP="${1}" 104 | local elem_list="${2}" 105 | 106 | # elem_list could start with ${ELEM_SEP}. so, remove it first 107 | elem_list=${elem_list#${ELEM_SEP}} 108 | 109 | local -a ret_list=() 110 | local elem="" 111 | while true; do 112 | elem=${elem_list%%${ELEM_SEP}*} 113 | elem_list=${elem_list#*${ELEM_SEP}} 114 | ret_list+=("${elem}") 115 | if [[ "${elem}" == "${elem_list}" ]]; then 116 | break 117 | fi 118 | done 119 | echo "${ret_list[@]}" 120 | } 121 | 122 | # convert path to patched file to .*.o.cmd file 123 | function util::cmd_file_path() 124 | { 125 | local -r FILE_PATH="${1}" 126 | echo "$(dirname "${FILE_PATH}")/.$(basename "${FILE_PATH}").cmd" 127 | } 128 | 129 | # do reverse operation of cmd_file_path 130 | function util::un_cmd_file_path() 131 | { 132 | local -r CMD_PATH="${1}" 133 | local CMD_FILE="$(basename "${CMD_PATH}")" 134 | CMD_FILE="${CMD_FILE:1}" # remove the prefix '.' in the name 135 | CMD_FILE="${CMD_FILE%%\.cmd}" # remove the suffix .o.cmd in the name 136 | echo "$(dirname "${CMD_PATH}")/${CMD_FILE}" 137 | } 138 | 139 | # takes two parameter: first: path to file "with" .o extension, second: 140 | # "name" of variable to return a name for object parent. 141 | # 142 | # Assumption: given patched file __does not__ have more than two 143 | # parents. e.g., obj.o is a part of library used for kmod1.ko, kmod2.ko, 144 | # ... 145 | # 146 | # NOTE: this function puts prefix, __, to its local variables to avoid 147 | # naming collision with the name for return value. 148 | function util::__find_obj_parent() 149 | { 150 | # given patched file is its own object parent. 151 | local __obj_parent="${1}" 152 | local -r __RETVAL="${2}" 153 | 154 | # starting from dir where the given patched file lives, search the dir 155 | # and its parent dir. 156 | local __parent_dir="$(dirname "${__obj_parent}")" 157 | local __cmd_file="$(util::cmd_file_path "${__obj_parent}")" 158 | while [[ ! -e "${__parent_dir}/MAINTAINERS" ]]; do 159 | util::log_info "Looking at ${__parent_dir} for parent object for ${__cmd_file}" 160 | 161 | # cmd files in parent dir except __cmd_file 162 | local -a __cmd_files=($(find "${__parent_dir}" -maxdepth 1 \ 163 | -name '.*.o.cmd' -a \ 164 | -not -name $(basename "${__cmd_file}"))) 165 | # now following .*.o.cmd file in ${__cmd_files[@]} to find object 166 | # parent. 167 | while [[ ${#__cmd_files[@]} != 0 ]]; do 168 | local __un_cmd_file="$(util::un_cmd_file_path "${__cmd_file}")" 169 | # file path to ko file path 170 | if [[ -f "${__un_cmd_file%.o}.ko" ]]; then 171 | # now found its parent! exit here 172 | eval "${__RETVAL}"="${__un_cmd_file%.o}.ko" 173 | return 174 | fi 175 | 176 | if ! grep -lw "${__un_cmd_file}" "${__cmd_files[@]}" >& /dev/null; then 177 | # no more parent is available for 178 | # ${__un_cmd_file}.o. hence, break to go to upper dir. 179 | break 180 | fi 181 | 182 | local -a __parent_cmd_files=($(grep -lw "${__un_cmd_file}" "${__cmd_files[@]}")) 183 | if [[ ${#__parent_cmd_files[@]} == 1 ]]; then 184 | __cmd_file="${__parent_cmd_files[0]}" 185 | __cmd_files=($(find "${__parent_dir}" -maxdepth 1 \ 186 | -name '.*.o.cmd' -a \ 187 | -not -name $(basename "${__cmd_file}"))) 188 | else 189 | util::error "${__un_cmd_file} has ${#__parent_cmd_files[@]} parents" 190 | fi 191 | done 192 | 193 | # goes to parent dir using `dirname` 194 | __parent_dir=$(dirname "${__parent_dir}") 195 | if [[ -z ${__parent_dir} ]]; then 196 | # this is required for the condition for while loop. 197 | __parent_dir="./" 198 | fi 199 | done 200 | 201 | # couldn't file kernel module w/ the changes in given patched 202 | # file. default to 'vmlinux' 203 | eval "${__RETVAL}"="${G_KERNEL_VMLINUX}" 204 | } 205 | -------------------------------------------------------------------------------- /livepatch-cc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Copyright 2021 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # Author: yonghyun@google.com (Yonghyun Hwang) 18 | # 19 | # This script intercepts CC command for kbuild's dir/file.o targets to compiles a LLVM IR 20 | # FILE. For the dir/file.o target, it replaces .c file with a LLVM IR file that distills 21 | # diffs between 'original' and 'patched'. This task is required to compile the IR file 22 | # with the same compilation flags as .c file. 23 | 24 | #------------------------------------------------------------- 25 | # Shell setting 26 | #------------------------------------------------------------- 27 | set -E 28 | 29 | #------------------------------------------------------------- 30 | # Global variables 31 | #------------------------------------------------------------- 32 | declare -r G_KLP_DIFF_SUFFIX='__klp_diff.ll' 33 | declare -r G_KLP_OBJ_SUFFIX='__klp_diff.o' 34 | 35 | #------------------------------------------------------------- 36 | # Function definitions 37 | #------------------------------------------------------------- 38 | function util::log_error() 39 | { 40 | echo "${G_LOG_HEADER}[ERR |$(date +'%m/%d %H:%M')]:: $@" >&2 41 | return 0 42 | } 43 | 44 | function util::error() 45 | { 46 | util::log_error "$@" 47 | return 1 48 | } 49 | 50 | function util::cleanup() 51 | { 52 | local errCode="${1:-}" 53 | local lineNum="${2:-}" 54 | 55 | [[ ${errCode} == 0 ]] || \ 56 | util::log_error "trap at line: ${lineNum}, with error:${errCode}." 57 | 58 | exit "${errCode}" 59 | } 60 | # enable exit trap for debugging purpose 61 | trap 'util::cleanup $? ${LINENO}' ERR INT TERM EXIT 62 | 63 | #------------------------------------------------------------- 64 | # Main starts here 65 | #------------------------------------------------------------- 66 | declare -r CC_CMD="${1}" 67 | shift 68 | declare -ar ORIG_ARGS=("$@") 69 | 70 | if [[ -z "${LIVEPATCH_COMPILE_PARAMS}" ]]; then 71 | # empty ${LIVEPATCH_COMPILE_PARAMS} means livepatch-cc is expected to 72 | # pass through a command. 73 | exec "${CC_CMD}" "${ORIG_ARGS[@]}" 74 | fi 75 | 76 | # .ll file is used to compute diff between original and patched. "-g" option makes clang 77 | # to generate calls to llvm.dbg.*. This results in unnecessary diffs between the original 78 | # and patched. So, removing "-g" options here. 79 | if [[ "${LIVEPATCH_COMPILE_PARAMS}" == "build_ll_file" ]]; then 80 | declare -a args=() 81 | while [ "$#" -gt 0 ]; do 82 | if [[ "${1}" != "-g" ]]; then 83 | args+=("${1}") 84 | fi 85 | shift 86 | done 87 | exec "${CC_CMD}" "${args[@]}" 88 | fi 89 | 90 | # for now, ${LIVEPATCH_COMPILE_PARAMS} == "build_distilled_file" means livepatch-cc is 91 | # expected to compile a LLVM IR file. At this point, kbuild thinks that it builds .c 92 | # file. So, livepatch-cc replaces .c file w/ LLVM IR file that distills the diffs for 93 | # kernel livepatch generation. 94 | declare -a args=() 95 | declare llvm_diff_file="" 96 | while [ "$#" -gt 0 ]; do 97 | if [[ -f "${1}" ]]; then 98 | [[ -z "${llvm_diff_file}" ]] || \ 99 | util::error "cannot have two .c files on a single command" 100 | 101 | if [[ -f "${1}${G_KLP_DIFF_SUFFIX}" ]]; then 102 | # now, located .c file. replace it w/ LLVM IR file. note that 103 | # this LLVM IR file should be already created by `livepatch 104 | # diff` command. 105 | llvm_diff_file="${1}" 106 | args+=("${llvm_diff_file}${G_KLP_DIFF_SUFFIX}") 107 | else 108 | args+=("${1}") 109 | fi 110 | else 111 | args+=("${1}") 112 | fi 113 | shift 114 | done 115 | 116 | if [[ -z "${llvm_diff_file}" ]]; then 117 | # kbuild builds other files due to a dependancy. pass through a command. 118 | exec "${CC_CMD}" "${ORIG_ARGS[@]}" 119 | fi 120 | 121 | # because kbuild expects outputs, such as dir/file.d, for dir/file.o target, 122 | # run an original command first. 123 | "${CC_CMD}" "${ORIG_ARGS[@]}" 124 | # note that the last -o option is picked up by compiler for output 125 | # filename. 126 | args+=("-o" "${llvm_diff_file}${G_KLP_OBJ_SUFFIX}") 127 | exec "${CC_CMD}" "${args[@]}" 128 | -------------------------------------------------------------------------------- /livepatch-compile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Copyright 2021 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # Author: yonghyun@google.com (Yonghyun Hwang) 18 | # 19 | # This script compiles LLVM IR file (or c file) to object file (or LLVM IR 20 | # file) using build command and options in given cmd_file, $kdir/.file.o.cmd. 21 | # If LLVM IR file, .ll extension, is given as input, this outputs object file. 22 | # In case of c file, .c extension, this outputs LLVM IR file. 23 | # 24 | # To build output, this parses $kdir/.file.o.cmd and uses command in it. This 25 | # script assumes that linux kernel is already built, which had created the 26 | # $kdir/.file.o.cmd files. Note that this script doesn't intercept CC cmd by 27 | # kbuild, which is different from livepatch-cc. 28 | 29 | #------------------------------------------------------------- 30 | # Shell setting 31 | #------------------------------------------------------------- 32 | set -E 33 | 34 | #------------------------------------------------------------- 35 | # Global variables 36 | #------------------------------------------------------------- 37 | declare -r G_KLP_COMPILE_CMD="$0" 38 | declare -r G_KLP_COMPILE_PATH=$(dirname $(readlink -f "${G_KLP_COMPILE_CMD}")) 39 | declare -r G_C_FILE_EXT='c' 40 | declare -r G_IR_FILE_EXT='ll' 41 | 42 | declare G_IN_FILE="" 43 | declare G_OUT_FILE="" 44 | declare G_CMD_FILE="" 45 | 46 | #------------------------------------------------------------- 47 | # Include library 48 | #------------------------------------------------------------- 49 | source "${G_KLP_COMPILE_PATH}/libutil.bash" "livepatch-compile" 50 | 51 | #------------------------------------------------------------- 52 | # Function definitions 53 | #------------------------------------------------------------- 54 | function cleanup() 55 | { 56 | local errCode="${1:-}" 57 | local lineNum="${2:-}" 58 | 59 | [[ ${errCode} == 0 ]] || \ 60 | util::log_error "trap at line: ${lineNum}, with error:${errCode}." 61 | 62 | exit "${errCode}" 63 | } 64 | # enable exit trap for debugging purpose 65 | trap 'cleanup $? ${LINENO}' ERR INT TERM EXIT 66 | 67 | function print_usage() 68 | { 69 | cat <& /dev/null || \ 178 | util::error "compiler, $(basename "${CC_CMD}"), is not available" 179 | 180 | # command options in the \$kdir/.file.o.cmd are to build .o target. To build .ll 181 | # file, '-c' option should be replaced with '-S' and '-emit-llvm'. 182 | declare -a args=() 183 | while [ "$#" -gt 0 ]; do 184 | case "${1}" in 185 | '-c') 186 | if [[ "${IN_FILE_EXT}" == "${G_C_FILE_EXT}" ]]; then 187 | args+=("-S" "-emit-llvm") 188 | else 189 | args+=("-c") 190 | fi 191 | ;; 192 | '-g') 193 | # .ll file is used to compute diff between original and 194 | # patched. "-g" option makes clang to generate calls to 195 | # llvm.dbg.*. This results in unnecessary diffs between the 196 | # original and patched. So, removing "-g" option for c file input. 197 | if [[ "${IN_FILE_EXT}" == "${G_IR_FILE_EXT}" ]]; then 198 | args+=("-g"); 199 | fi 200 | ;; 201 | *.c) 202 | declare -r C_FILE="${1}" 203 | [[ -f "${C_FILE}" ]] || \ 204 | util::error "${C_FILE} doesn't exists" 205 | # ${G_IN_FILE} doesn't need to be in the same dir for 206 | # ${C_FILE}. so, add include path here. 207 | declare -r HEADER_PATH="$(dirname "${C_FILE}")" 208 | args+=(-I "${HEADER_PATH}" "${G_IN_FILE}") 209 | ;; 210 | *) 211 | args+=("${1}"); 212 | esac 213 | shift 214 | done 215 | 216 | # note that the last -o option is picked up by compiler for output 217 | # filename. 218 | "${CC_CMD}" "${args[@]}" "-o" "${G_OUT_FILE}" 219 | util::log_ok "Built ${G_OUT_FILE}" 220 | -------------------------------------------------------------------------------- /main.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | * 18 | * This file implements main() for kernel livepatch generation. The main() 19 | * instantiates command object with given user arguments and run the command. 20 | */ 21 | #include 22 | #include 23 | 24 | #include "command.h" 25 | #include "llvm/Support/raw_ostream.h" 26 | 27 | int main(int argc, char **argv) 28 | { 29 | try { 30 | std::unique_ptr command = Command::Create(argc, argv); 31 | std::error_code ec = command->Run(); 32 | if (ec) { 33 | llvm::errs() << ec.message() << "\n"; 34 | return ec.value(); 35 | } 36 | } catch (std::error_code e) { 37 | llvm::errs() << e.message() << "\n"; 38 | return e.value(); 39 | } 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /symbol_map.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #include "symbol_map.h" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "auto_cleanup.h" 30 | #include "command.h" 31 | 32 | #include 33 | 34 | namespace 35 | { 36 | // Tokenize a given line from the output of `gen-symbol-map`. 37 | std::vector TokenizeSymbolLine(const std::string &line) 38 | { 39 | std::vector tokens; 40 | static const char *delim = " "; 41 | char *token = std::strtok(const_cast(line.c_str()), delim); 42 | while (token != nullptr) { 43 | tokens.push_back(std::string(token)); 44 | token = std::strtok(nullptr, delim); 45 | } 46 | return tokens; 47 | } 48 | } // namespace 49 | 50 | std::unique_ptr SymbolMap::Create(const std::string &filename) 51 | { 52 | if (filename.empty()) 53 | return nullptr; 54 | 55 | return std::make_unique(filename); 56 | } 57 | 58 | SymbolMap::SymbolMap(std::string_view filename) noexcept(false) 59 | { 60 | std::fstream file(filename.data(), std::ios::in); 61 | if (!file.is_open()) { 62 | throw std::error_code{ errno, std::system_category() }; 63 | } 64 | AutoCleanup fd_close([&file_ = file]() { file_.close(); }); 65 | 66 | std::string line; 67 | while (getline(file, line)) { 68 | auto tokens = TokenizeSymbolLine(line); 69 | if (tokens.size() != ElemIndex::NUM_OF_ELEMS + 1) { 70 | throw std::error_code{ 71 | Command::ErrorCode::INVALID_SYM_MAP 72 | }; 73 | } 74 | std::array sym_entry = { 75 | tokens[ElemIndex::MOD_NAME], 76 | tokens[ElemIndex::PATH], 77 | tokens[ElemIndex::SYMBOL], 78 | }; 79 | // last element is an alias to a symbol 80 | symbol_entries_.emplace(tokens[ElemIndex::NUM_OF_ELEMS], 81 | std::move(sym_entry)); 82 | } 83 | } 84 | 85 | const std::array & 86 | SymbolMap::QueryAlias(const std::string &alias) 87 | { 88 | if (symbol_entries_.find(alias) == symbol_entries_.end()) { 89 | throw std::error_code{ Command::ErrorCode::INVALID_SYM_MAP }; 90 | } 91 | 92 | return symbol_entries_[alias]; 93 | } 94 | -------------------------------------------------------------------------------- /symbol_map.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #ifndef SYMBOL_MAP_H_ 19 | #define SYMBOL_MAP_H_ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | // This class parses an output file of `gen-symbol-map` to construct internal 29 | // database for querying symbol along with name of the file that has the symbol 30 | // in it. The exoected output format by `gen-symbol-map` is as follows; 31 | // 32 | // ${mod_name} ${path_to_c_file} ${symbol} ${llpatch_alias} 33 | // test_klp kernel/livepatch/test/test-attr-apple.c fruit apple_fruit 34 | class SymbolMap final { 35 | public: 36 | enum ElemIndex { MOD_NAME = 0, PATH = 1, SYMBOL = 2, NUM_OF_ELEMS = 3 }; 37 | 38 | SymbolMap(std::string_view filename) noexcept(false); 39 | ~SymbolMap() = default; 40 | 41 | // Don't allow copy. 42 | SymbolMap(const SymbolMap &rhs) = delete; 43 | SymbolMap &operator=(const SymbolMap &rhs) = delete; 44 | 45 | // Returns array of (mod_name, path, symbol) for given llpatch alias 46 | // name. If no match found, it throws an exception 47 | const std::array & 48 | QueryAlias(const std::string &alias) noexcept(false); 49 | 50 | static std::unique_ptr Create(const std::string &filename); 51 | 52 | private: 53 | // key: alias name, value: array of (mod_name, path, symbol) 54 | std::unordered_map > 55 | symbol_entries_; 56 | }; 57 | 58 | #endif // SYMBOL_MAP_H_ 59 | -------------------------------------------------------------------------------- /templates/Makefile.tmpl: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2021 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # Author: yonghyun@google.com (Yonghyun Hwang) 17 | # 18 | # Makefile: Makefile template for building kernel live patch 19 | # geneation. llpatch script will replaces {{MARKERS}} to complete the 20 | # Makefile. 21 | # 22 | 23 | # path to linux kernel source tree 24 | KLP_BUILD = {{PATH_TO_LINUX_KERNEL_SOURCE_TREE}} 25 | # name of livepatch module 26 | KLP_NAME = {{NAME_OF_LIVEPATCH}} 27 | 28 | # this makefile assumes that both klp_patch.o and livepatch.c are already 29 | # created/generated by llpatch script by calling 'livepatch' command. 30 | # Hence, following rule don't need to be modified. 31 | obj-m += $(KLP_NAME).o 32 | 33 | ldflags-y += -T $(src)/livepatch.lds 34 | 35 | $(KLP_NAME)-objs += klp_patch.o livepatch.o 36 | 37 | all: 38 | make -C $(KLP_BUILD) M=$(PWD) modules 39 | 40 | clean: 41 | $(RM) $(KLP_NAME).ko $(KLP_NAME).mod.c $(KLP_NAME).mod.o \ 42 | $(KLP_NAME).o Module.symvers modules.order livepatch.o 43 | -------------------------------------------------------------------------------- /templates/README: -------------------------------------------------------------------------------- 1 | This directory contains template files used to generate kernel 2 | livepatch. There are three files under this directory. 3 | 4 | 1) livepatch.c.tmpl 5 | - livepatch wrapper linked to klp_patch.o file that contains 6 | implementation of livepatched functions. 7 | 8 | 2) livepatch.lds.tmpl 9 | - linker script that resolves address for the livepatced function. 10 | 11 | 3) Makefile.tmpl 12 | - makefile to build kernel livepatch. 13 | 14 | 4) llpatch.h 15 | - defines macro for LLpatch symbols 16 | 17 | 5) llpatch-callbacks.c 18 | - implements default callbacks for kernel livepatch 19 | - this can be tweaked to implement customized callbacks 20 | 21 | Each template contains {{MARKER}} in it hoping that they are replaced by 22 | either `llpatch` or `livepatch gen` command. 23 | -------------------------------------------------------------------------------- /templates/livepatch.c.tmpl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | * 18 | * livepatch.c: template for kernel live patch module. this file is used to 19 | * generate kernel livepatch. `livepatch gen` command will parse in this file 20 | * and replaces {{MARKERS}} to fill out "struct klp_func" and "struct 21 | * klp_object". 22 | * 23 | */ 24 | #include 25 | #include 26 | #include 27 | 28 | #include "{{LLPATCH_CALLBACKS}}" 29 | 30 | /* 31 | * List of livepatched functions. Note that function prototypes for all 32 | * livepatched functions are void livepatch_${name_of_func}(void) to avoid 33 | * header file inclusion for correct function prototypes. The address of 34 | * these functions will be resolved by the ld script, livepatch.lds. 35 | */ 36 | {{LIST_OF_LIVEPATCH_FUNCTIONS}} 37 | 38 | /* 39 | * List of livepatched functions. 40 | * If format of this struct (e.g., indentation or adding new element) is changed, 41 | * awk processing in `llpatch-merge` should be updated and synced. 42 | */ 43 | static struct klp_func funcs[] = { 44 | /* list of 45 | *{ 46 | * .old_name = "${name_of_func}", 47 | * .new_name = "livepatch_${name_of_func}", 48 | * .old_sympos = ${sympos}, 49 | *}, 50 | */ 51 | {{LIST_FOR_KLP_FUNC_STRUCT}} 52 | {} 53 | }; 54 | 55 | /* 56 | * TODO: List of livepatched objects. For now, only one entry 57 | * is allowed. This implies that current livepatch generation allows 58 | * changes in either a "single" kernel module or vmlinux. Not both or in 59 | * multple kernel modules. This restriction should be relaxed to allow 60 | * changes in multiple kernel modules and/or vmlinux. 61 | */ 62 | static struct klp_object objs[] = { 63 | { 64 | /* 65 | * NULL name NULL means vmlinux. unless, it's module name 66 | * .name = NULL, 67 | */ 68 | {{NAME_OF_OBJECT}} 69 | .funcs = funcs, 70 | .callbacks = { 71 | .pre_patch = pre_patch_callback, 72 | .post_patch = post_patch_callback, 73 | .pre_unpatch = pre_unpatch_callback, 74 | .post_unpatch = post_unpatch_callback, 75 | }, 76 | }, 77 | {} 78 | }; 79 | 80 | static struct klp_patch patch = { 81 | .mod = THIS_MODULE, 82 | .objs = objs, 83 | }; 84 | 85 | static int livepatch_init(void) 86 | { 87 | return klp_enable_patch(&patch); 88 | } 89 | 90 | static void livepatch_exit(void) {} 91 | 92 | module_init(livepatch_init); 93 | module_exit(livepatch_exit); 94 | MODULE_LICENSE("GPL"); 95 | MODULE_INFO(livepatch, "Y"); 96 | -------------------------------------------------------------------------------- /templates/livepatch.lds.tmpl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | * 18 | * livepatch.lds: template for linker script. this file is actually 19 | * empty. However, `livepatch gen` command should add list of functions to 20 | * resolve the address for livepatched functions. All livepatched functions 21 | * are declared as void ${func}(void) in livepatch.c while actual 22 | * implemenation is in klp_patch.o where __${func} lives. Hence the 23 | * following list should be available in this file. 24 | * 25 | * list of assignments for livepatched functions 26 | * ${func} = __${func} 27 | */ 28 | 29 | -------------------------------------------------------------------------------- /templates/llpatch-callbacks.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | /* 19 | * This file implements default callbacks for kernel livepatch. This file 20 | * is one of default inputs to `llpatch` for livepatch generation. If 21 | * custom callback functions are required, the following callback functions 22 | * can be customized and the updated llpatch-callbacks.c can be given to 23 | * `llpatch` when generating kernel livepatch. For `llpatch`, check 24 | * '-c|--callbacks' commandline option. 25 | * 26 | * While customizing the callbacks, access to global variables defined in 27 | * ${kmod} and/or vmlinux, two macros from llpatch.h can be entertained. 28 | * Those are LLPATCH_DECLARE_SYMBOL and LLPATCH_SYMBOL. Please see 29 | * llpatch.h for the macros and examples. 30 | * 31 | * For background, see kernel's Documentation/livepatch/callbacks.txt. 32 | * 33 | * IMPORTANT NOTES: 34 | * - DO NOT change name of callback functions. their names are used in 35 | * auto-generated livepatch wrapper. 36 | * - DO NOT remove any callback functions here. they are used in 37 | * auto-generated livepatch wrapper. 38 | * auto-generated livepatch wrapper. 39 | * - DO NOT remove any headers included here 40 | */ 41 | #include 42 | #include 43 | #include 44 | 45 | #include "llpatch.h" 46 | 47 | /* Executed on object patching */ 48 | static int pre_patch_callback(struct klp_object *obj) 49 | { 50 | return 0; 51 | } 52 | 53 | /* Executed after object patching */ 54 | static void post_patch_callback(struct klp_object *obj) 55 | { 56 | } 57 | 58 | /* Executed on object unpatching */ 59 | static void pre_unpatch_callback(struct klp_object *obj) 60 | { 61 | } 62 | 63 | /* Executed after object unpatching */ 64 | static void post_unpatch_callback(struct klp_object *obj) 65 | { 66 | } 67 | -------------------------------------------------------------------------------- /templates/llpatch.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #ifndef LLPATCH_H_ 19 | #define LLPATCH_H_ 20 | 21 | /* 22 | * This macro provides a way to access global variables defined in kmod or 23 | * vmlinux. This is highly useful if livepatch wants to access the variables 24 | * before/after applying the livepatch. One possible example would be to 25 | * access/obtain a lock before applying the livepatch. This also can be used to 26 | * add "shadow" variables for livepatch. Accessing the original bindings of the 27 | * variables which the patch will redefine, to allow a constructor for a new 28 | * shadow variable to initialize it from old value For background on the shadow 29 | * variable, see kernel's Documentation/livepatch/shadow-vars.txt. 30 | * 31 | * The following example shows how to access global variable defined in 32 | * ${KDIR}/kernel/livepatch/test/test-attr-apple.c in livepatch.c 33 | * 34 | * // in kernel/livepatch/test/test-attr-apple.c, part of test-klp.ko 35 | * static char fruit[PAGE_SIZE] = "apple"; 36 | * 37 | * // in the livepatch.c, livepatch wrapper 38 | * typedef char char_array_t[PAGE_SIZE]; 39 | * 40 | * LLPATCH_DECLARE_SYMBOL(char_array_t, my_fruit, kernel/livepatch/test/test-attr-apple.c, fruit); 41 | * ... 42 | * pr_info("hello llpatch symbol: %s\n", LLPATCH_SYMBOL(my_fruit)); 43 | * 44 | * IMPORTANT NOTES: 45 | * - __DO NOT__ put comments in this macro 46 | * - use the exactly __SAME__ type for variables to be accessed 47 | * - SYMBOL_PATH and SYMBOL_NAME specified here is processed by `gen-symbol-map` 48 | */ 49 | #define LLPATCH_DECLARE_SYMBOL(\ 50 | SYMBOL_TYPE, SYMBOL_ALIAS, SYMBOL_PATH, SYMBOL_NAME) \ 51 | extern SYMBOL_TYPE __llpatch_symbol_ ## SYMBOL_ALIAS 52 | 53 | #define LLPATCH_SYMBOL(SYMBOL_ALIAS) \ 54 | __llpatch_symbol_ ## SYMBOL_ALIAS 55 | 56 | #endif // LLPATCH_H_ 57 | -------------------------------------------------------------------------------- /thin_archive.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #include "thin_archive.h" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "auto_cleanup.h" 30 | #include "elf_error.h" 31 | #include "llvm/Support/raw_ostream.h" 32 | 33 | namespace 34 | { 35 | // Parses a given line assuming that line complies w/ posix output by nm. 36 | // Note that returned symbol type is always 'W' if it's weak symbol or object. 37 | std::pair 38 | ParseSymbolLine(const std::string& line) 39 | { 40 | size_t sym_end = line.find(' '); 41 | std::string symbol_name = line.substr(0, sym_end); 42 | 43 | char symbol_type = '?'; 44 | if (sym_end != std::string::npos) { 45 | size_t sym_type_pos = line.find_first_not_of(' ', sym_end); 46 | symbol_type = toupper(line.at(sym_type_pos)); 47 | } 48 | 49 | // V: The symbol is a weak object. W: The symbol is a weak symbol. 50 | // We need to know whether symbol is weak or not. So, use 'W' only 51 | symbol_type = (symbol_type == 'V') ? 'W' : symbol_type; 52 | return std::make_pair(symbol_name, symbol_type); 53 | } 54 | } // namespace 55 | 56 | std::unique_ptr ThinArchive::Create(const std::string &filename) 57 | { 58 | if (filename.empty()) 59 | return nullptr; 60 | 61 | return std::make_unique(filename); 62 | } 63 | 64 | ThinArchive::ThinArchive(std::string_view filename) noexcept(false) 65 | { 66 | std::fstream file(filename.data(), std::ios::in); 67 | if (!file.is_open()) { 68 | throw std::error_code{ errno, std::system_category() }; 69 | } 70 | AutoCleanup fd_close([&file_ = file]() { file_.close(); }); 71 | 72 | // Two pass algorithm to build unique_symbols_ and duplicated_symbols_ 73 | // Step 1: Build unique_symbols_ while finding duplicated symbols. 74 | std::unordered_set dup_symbols; 75 | std::unordered_set non_weak_symbols; 76 | std::string line; 77 | while (getline(file, line)) { 78 | auto [symbol_name, symbol_type] = ParseSymbolLine(line); 79 | if (unique_symbols_.find(symbol_name) == 80 | unique_symbols_.end()) { 81 | unique_symbols_.emplace(symbol_name); 82 | 83 | if (symbol_type != 'W') { 84 | non_weak_symbols.emplace(std::move(symbol_name)); 85 | } 86 | continue; 87 | } 88 | 89 | if (symbol_type == 'W') { 90 | continue; 91 | } 92 | 93 | // If symbol in unique_symbols_ is not weak, it's duplicated symbol. 94 | if (non_weak_symbols.find(symbol_name) != 95 | non_weak_symbols.end()) { 96 | dup_symbols.emplace(std::move(symbol_name)); 97 | } 98 | 99 | non_weak_symbols.emplace(std::move(symbol_name)); 100 | } 101 | for (const std::string &i : dup_symbols) { 102 | unique_symbols_.erase(i); 103 | } 104 | 105 | // Step 2: Build duplicated symbols by inserting filename to 106 | // duplicated_symbols_. 107 | std::string current_filename; 108 | // matching format example: built-in.a[arch/x86/kernel/head_64.o]: 109 | std::regex file_path_match(".+\\.a\\[.+\\.o\\]:"); 110 | std::unordered_set same_sym_file; 111 | std::string symbol_name; 112 | file.clear(); 113 | file.seekg(0); 114 | while (getline(file, line)) { 115 | if (std::regex_match(line, file_path_match)) { 116 | auto pos_start = line.find("[") + 1; 117 | auto pos_end = line.find("]"); 118 | current_filename = 119 | line.substr(pos_start, pos_end - pos_start); 120 | continue; 121 | } 122 | 123 | symbol_name = line.substr(0, line.find(" ")); 124 | if (unique_symbols_.find(symbol_name) != unique_symbols_.end()) { 125 | continue; 126 | } 127 | 128 | auto sym_file = std::string(symbol_name) + 129 | std::string(current_filename); 130 | if (same_sym_file.find(sym_file) == 131 | same_sym_file.end()) { 132 | same_sym_file.insert(sym_file); 133 | } else { 134 | // Oops. this ELF has same symbol+filename combination, 135 | // which cannot be handled. :'( throw exception. 136 | llvm::outs() 137 | << "sym: " << symbol_name 138 | << ", filename: " << current_filename 139 | << "\n"; 140 | throw std::error_code{ 141 | ElfErrorCode::SAME_SYMBOL_FILENAME 142 | }; 143 | } 144 | 145 | duplicated_symbols_[symbol_name].push_back( 146 | current_filename); 147 | } 148 | } 149 | 150 | int ThinArchive::QuerySymbol(const std::string &symbol, 151 | const std::string &filename) 152 | { 153 | if (unique_symbols_.find(symbol) != unique_symbols_.end()) { 154 | // pos for unique symbols is always 0 155 | return 0; 156 | } 157 | 158 | auto dup_symbol = duplicated_symbols_.find(symbol); 159 | if (dup_symbol != duplicated_symbols_.end()) { 160 | int pos = 1; 161 | for (std::string_view fn : dup_symbol->second) { 162 | if (filename == fn) { 163 | return pos; 164 | } 165 | pos++; 166 | } 167 | } 168 | 169 | // No match found for given symbol && filename. return negative value 170 | // to indicate that. 171 | return -1; 172 | } 173 | -------------------------------------------------------------------------------- /thin_archive.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * Author: Yonghyun Hwang 17 | */ 18 | #ifndef THIN_ARCHIVE_H_ 19 | #define THIN_ARCHIVE_H_ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | // This class parses an output file of `nm` to construct internal database 29 | // for querying symbol along with name of the file that has the symbol in 30 | // it. The class assumes the "posix" output format by `nm -f posix`. The 31 | // format is as follows; 32 | // 33 | // ${thin_archive_name}.a[${full_path_to_obj_file}]: 34 | // ${symbol} ${symbol_type} ${symbol_value} 35 | // ${symbol1} ${symbol_type1} ${symbol_value1} 36 | // ... 37 | // ${thin_archive_name}.a[${full_path_to_obj_file2}]: 38 | // ... 39 | // built-in.a[arch/x86/events/intel/core.o]: 40 | // allow_tsx_force_abort d 2b8 1 41 | // any_show t 38f0 24 42 | // 43 | // This class takes ${full_path_to_obj_file} and ${symbol}s only to service the query. To 44 | // generate the text file, output of nm, for this class, use the following command. 45 | // 46 | // $ nm -f posix --defined-only ${built-in}.a 47 | class ThinArchive final { 48 | public: 49 | ThinArchive(std::string_view filename) noexcept(false); 50 | ~ThinArchive() = default; 51 | 52 | // Don't allow copy. 53 | ThinArchive(const ThinArchive &rhs) = delete; 54 | ThinArchive &operator=(const ThinArchive &rhs) = delete; 55 | 56 | // Returns pos for given symbol and filename. The pos is used to construct KLP 57 | // symbol name or pos for livepatched function. Note that internal database for 58 | // symbols is aware of relocatable symbols, such as UND, OBJECT and FUNC. filename 59 | // is used to match duplicated symbols. NOTE that filename is ignored if symbol is 60 | // unique. If no symbol found, returns negative value. 61 | int QuerySymbol(const std::string &symbol, const std::string &filename); 62 | 63 | static std::unique_ptr Create(const std::string &filename); 64 | 65 | private: 66 | std::unordered_set unique_symbols_; 67 | // key: symbol name, value: list of filename 68 | std::unordered_map > 69 | duplicated_symbols_; 70 | }; 71 | 72 | #endif // THIN_ARCHIVE_H_ 73 | -------------------------------------------------------------------------------- /third_party/llvm-diff/DiffConsumer.cpp: -------------------------------------------------------------------------------- 1 | //===-- DiffConsumer.cpp - Difference Consumer ------------------*- C++ -*-===// 2 | // 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | // 9 | // This files implements the LLVM difference Consumer 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | #include "DiffConsumer.h" 14 | #include "llvm/IR/Instructions.h" 15 | #include "llvm/Support/Debug.h" 16 | #include "llvm/Support/ErrorHandling.h" 17 | 18 | using namespace llvm; 19 | 20 | static void ComputeNumbering(const Function *F, 21 | DenseMap &Numbering) { 22 | unsigned IN = 0; 23 | 24 | // Arguments get the first numbers. 25 | for (const auto &Arg : F->args()) 26 | if (!Arg.hasName()) 27 | Numbering[&Arg] = IN++; 28 | 29 | // Walk the basic blocks in order. 30 | for (const auto &Func : *F) { 31 | if (!Func.hasName()) 32 | Numbering[&Func] = IN++; 33 | 34 | // Walk the instructions in order. 35 | for (const auto &BB : Func) 36 | // void instructions don't get numbers. 37 | if (!BB.hasName() && !BB.getType()->isVoidTy()) 38 | Numbering[&BB] = IN++; 39 | } 40 | 41 | assert(!Numbering.empty() && "asked for numbering but numbering was no-op"); 42 | } 43 | 44 | void Consumer::anchor() { } 45 | 46 | void DiffConsumer::printValue(const Value *V, bool isL) { 47 | if (V->hasName()) { 48 | out << (isa(V) ? '@' : '%') << V->getName(); 49 | return; 50 | } 51 | if (V->getType()->isVoidTy()) { 52 | if (auto *SI = dyn_cast(V)) { 53 | out << "store to "; 54 | printValue(SI->getPointerOperand(), isL); 55 | } else if (auto *CI = dyn_cast(V)) { 56 | out << "call to "; 57 | printValue(CI->getCalledOperand(), isL); 58 | } else if (auto *II = dyn_cast(V)) { 59 | out << "invoke to "; 60 | printValue(II->getCalledOperand(), isL); 61 | } else { 62 | out << *V; 63 | } 64 | return; 65 | } 66 | if (isa(V)) { 67 | out << *V; 68 | return; 69 | } 70 | 71 | unsigned N = contexts.size(); 72 | while (N > 0) { 73 | --N; 74 | DiffContext &ctxt = contexts[N]; 75 | if (!ctxt.IsFunction) continue; 76 | if (isL) { 77 | if (ctxt.LNumbering.empty()) 78 | ComputeNumbering(cast(ctxt.L), ctxt.LNumbering); 79 | out << '%' << ctxt.LNumbering[V]; 80 | return; 81 | } else { 82 | if (ctxt.RNumbering.empty()) 83 | ComputeNumbering(cast(ctxt.R), ctxt.RNumbering); 84 | out << '%' << ctxt.RNumbering[V]; 85 | return; 86 | } 87 | } 88 | 89 | out << ""; 90 | } 91 | 92 | void DiffConsumer::header() { 93 | if (contexts.empty()) return; 94 | for (SmallVectorImpl::iterator 95 | I = contexts.begin(), E = contexts.end(); I != E; ++I) { 96 | if (I->Differences) continue; 97 | if (isa(I->L)) { 98 | // Extra newline between functions. 99 | if (Differences) out << "\n"; 100 | 101 | const Function *L = cast(I->L); 102 | const Function *R = cast(I->R); 103 | if (L->getName() != R->getName()) 104 | out << "in function " << L->getName() 105 | << " / " << R->getName() << ":\n"; 106 | else 107 | out << "in function " << L->getName() << ":\n"; 108 | } else if (isa(I->L)) { 109 | const BasicBlock *L = cast(I->L); 110 | const BasicBlock *R = cast(I->R); 111 | if (L->hasName() && R->hasName() && L->getName() == R->getName()) 112 | out << " in block %" << L->getName() << ":\n"; 113 | else { 114 | out << " in block "; 115 | printValue(L, true); 116 | out << " / "; 117 | printValue(R, false); 118 | out << ":\n"; 119 | } 120 | } else if (isa(I->L)) { 121 | out << " in instruction "; 122 | printValue(I->L, true); 123 | out << " / "; 124 | printValue(I->R, false); 125 | out << ":\n"; 126 | } 127 | 128 | I->Differences = true; 129 | } 130 | } 131 | 132 | void DiffConsumer::indent() { 133 | unsigned N = Indent; 134 | while (N--) out << ' '; 135 | } 136 | 137 | bool DiffConsumer::hadDifferences() const { 138 | return Differences; 139 | } 140 | 141 | void DiffConsumer::reset() { 142 | contexts.clear(); 143 | Differences = false; 144 | Indent = 0; 145 | } 146 | 147 | void DiffConsumer::enterContext(const Value *L, const Value *R) { 148 | contexts.push_back(DiffContext(L, R)); 149 | Indent += 2; 150 | } 151 | 152 | void DiffConsumer::exitContext() { 153 | Differences |= contexts.back().Differences; 154 | contexts.pop_back(); 155 | Indent -= 2; 156 | } 157 | 158 | void DiffConsumer::log(StringRef text) { 159 | header(); 160 | indent(); 161 | out << text << '\n'; 162 | } 163 | 164 | void DiffConsumer::logf(const LogBuilder &Log) { 165 | header(); 166 | indent(); 167 | 168 | unsigned arg = 0; 169 | 170 | StringRef format = Log.getFormat(); 171 | while (true) { 172 | size_t percent = format.find('%'); 173 | if (percent == StringRef::npos) { 174 | out << format; 175 | break; 176 | } 177 | assert(format[percent] == '%'); 178 | 179 | if (percent > 0) out << format.substr(0, percent); 180 | 181 | switch (format[percent+1]) { 182 | case '%': out << '%'; break; 183 | case 'l': printValue(Log.getArgument(arg++), true); break; 184 | case 'r': printValue(Log.getArgument(arg++), false); break; 185 | default: llvm_unreachable("unknown format character"); 186 | } 187 | 188 | format = format.substr(percent+2); 189 | } 190 | 191 | out << '\n'; 192 | } 193 | 194 | void DiffConsumer::logd(const DiffLogBuilder &Log) { 195 | header(); 196 | 197 | for (unsigned I = 0, E = Log.getNumLines(); I != E; ++I) { 198 | indent(); 199 | switch (Log.getLineKind(I)) { 200 | case DC_match: 201 | out << " "; 202 | Log.getLeft(I)->print(out); out << '\n'; 203 | //printValue(Log.getLeft(I), true); 204 | break; 205 | case DC_left: 206 | out << "< "; 207 | Log.getLeft(I)->print(out); out << '\n'; 208 | //printValue(Log.getLeft(I), true); 209 | break; 210 | case DC_right: 211 | out << "> "; 212 | Log.getRight(I)->print(out); out << '\n'; 213 | //printValue(Log.getRight(I), false); 214 | break; 215 | } 216 | //out << "\n"; 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /third_party/llvm-diff/DiffConsumer.h: -------------------------------------------------------------------------------- 1 | //===-- DiffConsumer.h - Difference Consumer --------------------*- C++ -*-===// 2 | // 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | // 9 | // This header defines the interface to the LLVM difference Consumer 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | #ifndef LLVM_TOOLS_LLVM_DIFF_DIFFCONSUMER_H 14 | #define LLVM_TOOLS_LLVM_DIFF_DIFFCONSUMER_H 15 | 16 | #include "DiffLog.h" 17 | #include "llvm/ADT/DenseMap.h" 18 | #include "llvm/ADT/SmallVector.h" 19 | #include "llvm/IR/Value.h" 20 | #include "llvm/Support/Casting.h" 21 | #include "llvm/Support/raw_ostream.h" 22 | 23 | namespace llvm { 24 | class StringRef; 25 | class Module; 26 | class Value; 27 | class Function; 28 | 29 | /// The interface for consumers of difference data. 30 | class Consumer { 31 | virtual void anchor(); 32 | public: 33 | /// Record that a local context has been entered. Left and 34 | /// Right are IR "containers" of some sort which are being 35 | /// considered for structural equivalence: global variables, 36 | /// functions, blocks, instructions, etc. 37 | virtual void enterContext(const Value *Left, const Value *Right) = 0; 38 | 39 | /// Record that a local context has been exited. 40 | virtual void exitContext() = 0; 41 | 42 | /// Check whether there are differences. 43 | virtual bool hadDifferences() const = 0; 44 | 45 | /// Reset differences. 46 | virtual void reset() = 0; 47 | 48 | /// Record a difference within the current context. 49 | virtual void log(StringRef Text) = 0; 50 | 51 | /// Record a formatted difference within the current context. 52 | virtual void logf(const LogBuilder &Log) = 0; 53 | 54 | /// Record a line-by-line instruction diff. 55 | virtual void logd(const DiffLogBuilder &Log) = 0; 56 | 57 | protected: 58 | virtual ~Consumer() {} 59 | }; 60 | 61 | class DiffConsumer : public Consumer { 62 | private: 63 | struct DiffContext { 64 | DiffContext(const Value *L, const Value *R) 65 | : L(L), R(R), Differences(false), IsFunction(isa(L)) {} 66 | const Value *L; 67 | const Value *R; 68 | bool Differences; 69 | bool IsFunction; 70 | DenseMap LNumbering; 71 | DenseMap RNumbering; 72 | }; 73 | 74 | raw_ostream &out; 75 | SmallVector contexts; 76 | bool Differences; 77 | unsigned Indent; 78 | 79 | void printValue(const Value *V, bool isL); 80 | void header(); 81 | void indent(); 82 | 83 | public: 84 | DiffConsumer(raw_ostream &out) 85 | : out(out), Differences(false), Indent(0) {} 86 | ~DiffConsumer() override {} 87 | 88 | void reset() override; 89 | bool hadDifferences() const override; 90 | void enterContext(const Value *L, const Value *R) override; 91 | void exitContext() override; 92 | void log(StringRef text) override; 93 | void logf(const LogBuilder &Log) override; 94 | void logd(const DiffLogBuilder &Log) override; 95 | }; 96 | } 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /third_party/llvm-diff/DiffLog.cpp: -------------------------------------------------------------------------------- 1 | //===-- DiffLog.h - Difference Log Builder and accessories ------*- C++ -*-===// 2 | // 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | // 9 | // This header defines the interface to the LLVM difference log builder. 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | #include "DiffLog.h" 14 | #include "DiffConsumer.h" 15 | #include "llvm/ADT/StringRef.h" 16 | 17 | using namespace llvm; 18 | 19 | LogBuilder::~LogBuilder() { 20 | if (consumer) 21 | consumer->logf(*this); 22 | } 23 | 24 | StringRef LogBuilder::getFormat() const { return Format; } 25 | 26 | unsigned LogBuilder::getNumArguments() const { return Arguments.size(); } 27 | const Value *LogBuilder::getArgument(unsigned I) const { return Arguments[I]; } 28 | 29 | DiffLogBuilder::~DiffLogBuilder() { consumer.logd(*this); } 30 | 31 | void DiffLogBuilder::addMatch(const Instruction *L, const Instruction *R) { 32 | Diff.push_back(DiffRecord(L, R)); 33 | } 34 | void DiffLogBuilder::addLeft(const Instruction *L) { 35 | // HACK: VS 2010 has a bug in the stdlib that requires this. 36 | Diff.push_back(DiffRecord(L, DiffRecord::second_type(nullptr))); 37 | } 38 | void DiffLogBuilder::addRight(const Instruction *R) { 39 | // HACK: VS 2010 has a bug in the stdlib that requires this. 40 | Diff.push_back(DiffRecord(DiffRecord::first_type(nullptr), R)); 41 | } 42 | 43 | unsigned DiffLogBuilder::getNumLines() const { return Diff.size(); } 44 | 45 | DiffChange DiffLogBuilder::getLineKind(unsigned I) const { 46 | return (Diff[I].first ? (Diff[I].second ? DC_match : DC_left) 47 | : DC_right); 48 | } 49 | const Instruction *DiffLogBuilder::getLeft(unsigned I) const { 50 | return Diff[I].first; 51 | } 52 | const Instruction *DiffLogBuilder::getRight(unsigned I) const { 53 | return Diff[I].second; 54 | } 55 | -------------------------------------------------------------------------------- /third_party/llvm-diff/DiffLog.h: -------------------------------------------------------------------------------- 1 | //===-- DiffLog.h - Difference Log Builder and accessories ------*- C++ -*-===// 2 | // 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | // 9 | // This header defines the interface to the LLVM difference log builder. 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | #ifndef LLVM_TOOLS_LLVM_DIFF_DIFFLOG_H 14 | #define LLVM_TOOLS_LLVM_DIFF_DIFFLOG_H 15 | 16 | #include "llvm/ADT/SmallVector.h" 17 | #include "llvm/ADT/StringRef.h" 18 | 19 | namespace llvm { 20 | class Instruction; 21 | class Value; 22 | class Consumer; 23 | 24 | /// Trichotomy assumption 25 | enum DiffChange { DC_match, DC_left, DC_right }; 26 | 27 | /// A temporary-object class for building up log messages. 28 | class LogBuilder { 29 | Consumer *consumer; 30 | 31 | /// The use of a stored StringRef here is okay because 32 | /// LogBuilder should be used only as a temporary, and as a 33 | /// temporary it will be destructed before whatever temporary 34 | /// might be initializing this format. 35 | StringRef Format; 36 | 37 | SmallVector Arguments; 38 | 39 | public: 40 | LogBuilder(Consumer &c, StringRef Format) : consumer(&c), Format(Format) {} 41 | LogBuilder(LogBuilder &&L) 42 | : consumer(L.consumer), Format(L.Format), 43 | Arguments(std::move(L.Arguments)) { 44 | L.consumer = nullptr; 45 | } 46 | 47 | LogBuilder &operator<<(const Value *V) { 48 | Arguments.push_back(V); 49 | return *this; 50 | } 51 | 52 | ~LogBuilder(); 53 | 54 | StringRef getFormat() const; 55 | unsigned getNumArguments() const; 56 | const Value *getArgument(unsigned I) const; 57 | }; 58 | 59 | /// A temporary-object class for building up diff messages. 60 | class DiffLogBuilder { 61 | typedef std::pair DiffRecord; 62 | SmallVector Diff; 63 | 64 | Consumer &consumer; 65 | 66 | public: 67 | DiffLogBuilder(Consumer &c) : consumer(c) {} 68 | ~DiffLogBuilder(); 69 | 70 | void addMatch(const Instruction *L, const Instruction *R); 71 | // HACK: VS 2010 has a bug in the stdlib that requires this. 72 | void addLeft(const Instruction *L); 73 | void addRight(const Instruction *R); 74 | 75 | unsigned getNumLines() const; 76 | DiffChange getLineKind(unsigned I) const; 77 | const Instruction *getLeft(unsigned I) const; 78 | const Instruction *getRight(unsigned I) const; 79 | }; 80 | 81 | } 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /third_party/llvm-diff/DifferenceEngine.h: -------------------------------------------------------------------------------- 1 | //===-- DifferenceEngine.h - Module comparator ------------------*- C++ -*-===// 2 | // 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 | // See https://llvm.org/LICENSE.txt for license information. 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 | // 7 | //===----------------------------------------------------------------------===// 8 | // 9 | // This header defines the interface to the LLVM difference engine, 10 | // which structurally compares functions within a module. 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | #ifndef LLVM_TOOLS_LLVM_DIFF_DIFFERENCEENGINE_H 15 | #define LLVM_TOOLS_LLVM_DIFF_DIFFERENCEENGINE_H 16 | 17 | #include "DiffConsumer.h" 18 | #include "DiffLog.h" 19 | #include "llvm/ADT/StringRef.h" 20 | #include 21 | 22 | namespace llvm { 23 | class Function; 24 | class GlobalValue; 25 | class Instruction; 26 | class LLVMContext; 27 | class Module; 28 | class Twine; 29 | class Value; 30 | 31 | /// A class for performing structural comparisons of LLVM assembly. 32 | class DifferenceEngine { 33 | public: 34 | /// A RAII object for recording the current context. 35 | struct Context { 36 | Context(DifferenceEngine &Engine, const Value *L, const Value *R) 37 | : Engine(Engine) { 38 | Engine.consumer.enterContext(L, R); 39 | } 40 | 41 | ~Context() { 42 | Engine.consumer.exitContext(); 43 | } 44 | 45 | private: 46 | DifferenceEngine &Engine; 47 | }; 48 | 49 | /// An oracle for answering whether two values are equivalent as 50 | /// operands. 51 | class Oracle { 52 | virtual void anchor(); 53 | public: 54 | virtual bool operator()(const Value *L, const Value *R) = 0; 55 | 56 | protected: 57 | virtual ~Oracle() {} 58 | }; 59 | 60 | DifferenceEngine(Consumer &consumer) 61 | : consumer(consumer), globalValueOracle(nullptr) {} 62 | 63 | void diff(const Module *L, const Module *R); 64 | void diff(const Function *L, const Function *R); 65 | void log(StringRef text) { 66 | consumer.log(text); 67 | } 68 | LogBuilder logf(StringRef text) { 69 | return LogBuilder(consumer, text); 70 | } 71 | Consumer& getConsumer() const { return consumer; } 72 | 73 | /// Installs an oracle to decide whether two global values are 74 | /// equivalent as operands. Without an oracle, global values are 75 | /// considered equivalent as operands precisely when they have the 76 | /// same name. 77 | void setGlobalValueOracle(Oracle *oracle) { 78 | globalValueOracle = oracle; 79 | } 80 | 81 | /// Determines whether two global values are equivalent. 82 | bool equivalentAsOperands(const GlobalValue *L, const GlobalValue *R); 83 | 84 | private: 85 | Consumer &consumer; 86 | Oracle *globalValueOracle; 87 | }; 88 | } 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /third_party/llvm-diff/Makefile: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------- 2 | # Makefile: makefile template 3 | # 4 | # Copyright 2016 by Yonghyun Hwang 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. Some rights reserved. 10 | # ------------------------------------------------------------------------- 11 | 12 | # 0: list of source codes 13 | # - if specified, only those are compiled 14 | # - if not specified, all *.c, *.cpp, and *.cc will be compiled 15 | SRCS = 16 | 17 | # 1: list of sub directories 18 | # - if specified, make recursively for the sub dirs 19 | SUB_DIRS = 20 | 21 | # 2: list of compile options (e.g., -Ddefine, -Iinc, ...) 22 | LOCAL_CXXFLAGS = -I/usr/lib/llvm-11/include 23 | LOCAL_CFLAGS = 24 | 25 | # 3: list of link options (e.g., -lm, -Labc, ...) 26 | LOCAL_LIB = -L/usr/lib/llvm-11/lib -lLLVM 27 | 28 | # 4: name for a.out or library 29 | # - specify LIB_NAME if you want to create libLIB_NAME.so out of your SRCS 30 | # - specify EXE if you want to create executable binary w/ the name, EXE 31 | # - DO __NOT__ specify both of them. if nothing specified, EXE = project 32 | LIB_NAME = llvm-diff 33 | EXE = 34 | 35 | # 5: specify compiler 36 | # - if not specified, CC = gcc, CXX= g++ 37 | CC = 38 | CXX = clang++ 39 | 40 | # 6: specify path to dir w/ Makefile.rules and Makefile.macros in it 41 | PRJ_ROOT_DIR ?= ../.. 42 | 43 | # -------------------------------------------- 44 | # Magic part: please don't touch if you don't know what you are doing 45 | # -------------------------------------------- 46 | include $(PRJ_ROOT_DIR)/Makefile.rules 47 | $(eval $(call play_magic,$(EXE))) 48 | -------------------------------------------------------------------------------- /third_party/llvm-diff/README.md: -------------------------------------------------------------------------------- 1 | This directory contains source codes for llvm-diff. Initial code, 2 | Diff*.cpp, came from llvm project. Git tag for the imported code is 3 | "llvmorg-13-init". The latest source code for the llvm project is avaiable 4 | at https://github.com/llvm/llvm-project. Please note that the llvm-diff 5 | here will be adapted to work for kernel livepatch generation. Hence it's 6 | diverged from the upstream llvm-diff. 7 | -------------------------------------------------------------------------------- /update-patch: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Copyright 2021 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # Author: yonghyun@google.com (Yonghyun Hwang) 18 | # 19 | # This script takes patch file and updates it to avoid fuzz matching for 20 | # patching. For the update, this applies the given patch to kernel 21 | # repository and creates a new patch. The updated patch file is required 22 | # for kernel livepatch generation where fuzz matching for the patching is 23 | # not allowed. 24 | 25 | #------------------------------------------------------------- 26 | # Shell setting 27 | #------------------------------------------------------------- 28 | set -E 29 | 30 | #------------------------------------------------------------- 31 | # Global variables 32 | #------------------------------------------------------------- 33 | declare -r G_KDIR="$(pwd)" 34 | declare -r G_UPDATE_PATCH_CMD="$0" 35 | declare -r G_UPDATE_PATCH_PATH=$(dirname $(readlink -f "${G_UPDATE_PATCH_CMD}")) 36 | declare -r G_OUT_FILE="${2:-}" 37 | declare -r G_TMP_FILE="$(mktemp -t update_patch_tmp.XXXXXXXXXX)" 38 | declare -r G_TMP_MSG_FILE="$(mktemp -t commit_message.XXXXXXXXXX)" 39 | 40 | declare G_IN_FILE="${1:-}" 41 | declare -i G_PATCHED_DIRTY=0 42 | declare G_COMMIT_HASH="" 43 | 44 | #------------------------------------------------------------- 45 | # Include library 46 | #------------------------------------------------------------- 47 | source "${G_UPDATE_PATCH_PATH}/libutil.bash" "update-patch" 48 | 49 | #------------------------------------------------------------- 50 | # Function definitions 51 | #------------------------------------------------------------- 52 | function cleanup() 53 | { 54 | local errCode="${1:-}" 55 | local lineNum="${2:-}" 56 | 57 | [[ ${errCode} == 0 ]] || \ 58 | util::log_error "trap at line: ${lineNum}, with error:${errCode}." 59 | 60 | if [[ ${G_PATCHED_DIRTY} == 1 ]]; then 61 | patch -R -p1 -s -d "${G_KDIR}" -i "${G_IN_FILE}" 62 | G_PATCHED_DIRTY=0 63 | util::log_ok "Patch is reverted" 64 | fi 65 | 66 | [[ -z "${G_COMMIT_HASH}" ]] || 67 | git reset -q --hard "${G_COMMIT_HASH}" 68 | 69 | rm -f "${G_TMP_MSG_FILE}" "${G_TMP_FILE}" 70 | 71 | exit "${errCode}" 72 | } 73 | # enable exit trap for debugging purpose 74 | trap 'cleanup $? ${LINENO}' ERR INT TERM EXIT 75 | 76 | function print_usage() 77 | { 78 | cat <& /dev/null || \ 102 | util::error "Failed to apply patch" 103 | 104 | # don't allow overwrite 105 | [[ ! -f "${G_OUT_FILE}" ]] || \ 106 | util::error "Output file already exists." 107 | 108 | # make sure that git repository is clean 109 | if [[ $(git status -s | wc -l) != 0 ]]; then 110 | git status 111 | util::error "Please clean up git repository" 112 | fi 113 | 114 | G_COMMIT_HASH="$(git log -1 --format="%H")" 115 | } 116 | 117 | #------------------------------------------------------------- 118 | # Main starts here 119 | #------------------------------------------------------------- 120 | if [[ $# < 2 ]]; then 121 | print_usage 122 | exit 0 123 | fi 124 | 125 | validate_prepare 126 | 127 | util::log_info "Applying a patch, $(basename "${G_IN_FILE}")" 128 | patch -N -p1 -d "${G_KDIR}" -i "${G_IN_FILE}" 129 | G_PATCHED_DIRTY=1 130 | util::log_ok "Patch is applied" 131 | 132 | declare author="" 133 | declare email="" 134 | declare subject="" 135 | while read line; do 136 | case "${line}" in 137 | "Author: "*) 138 | author="${line#Author: }" 139 | util::log_info "Author: ${author}" 140 | ;; 141 | "Email: "*) 142 | email="${line#Email: }" 143 | util::log_info "Email: ${email}" 144 | ;; 145 | "Subject: "*) 146 | subject="${line#Subject: }" 147 | util::log_info "Subject: ${subject}" 148 | ;; 149 | *) 150 | break; 151 | esac 152 | done < <(cat "${G_IN_FILE}" | git mailinfo "${G_TMP_FILE}" /dev/null) 153 | 154 | if [[ -z "${author}" || -z "${email}" || -z "${subject}" ]]; then 155 | util::log_info "author|email|subject is not available for git patch" 156 | git diff >| "${G_OUT_FILE}" 157 | util::log_ok "Generated a simple diff, ${G_OUT_FILE}" 158 | exit 0 159 | fi 160 | 161 | util::log_info "Commit message" 162 | { 163 | echo "${subject}" 164 | echo "" 165 | cat "${G_TMP_FILE}" 166 | } >| "${G_TMP_MSG_FILE}" 167 | 168 | cat "${G_TMP_MSG_FILE}" 169 | echo --------------- 170 | 171 | util::log_info "Create a temporary commit" 172 | { 173 | git add . 174 | git commit -F "${G_TMP_MSG_FILE}" --author="${author} <${email}>" 175 | } >& /dev/null 176 | 177 | G_PATCHED_DIRTY=0 178 | 179 | util::log_info "Generate a patch and remove the temporary commit" 180 | git format-patch -n1 --output="${G_OUT_FILE}" HEAD >& /dev/null 181 | 182 | util::log_ok "Generated a new patch, ${G_OUT_FILE}" 183 | --------------------------------------------------------------------------------