├── RuntimeWrapping └── main.c ├── RuntimeWrapping.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── RuntimeWrapping.xccheckout └── project.pbxproj ├── .gitignore ├── README.md ├── WrappedLibc └── printf_wrapped.c └── LICENSE /RuntimeWrapping/main.c: -------------------------------------------------------------------------------- 1 | // Test driver 2 | 3 | #include 4 | 5 | int main(int argc, const char * argv[]) 6 | { 7 | printf("Hello, World!\n"); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /RuntimeWrapping.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | */build/* 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | profile 14 | *.moved-aside 15 | DerivedData 16 | .idea/ 17 | *.hmap 18 | *.xccheckout 19 | 20 | #CocoaPods 21 | Pods 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | RuntimeWrapping 2 | === 3 | 4 | This project demonstrates how to wrap a C function -- specifically, `printf(3)`. 5 | 6 | Here's how it works: 7 | 8 | 1. `WrappedLibc` exports a `printf` function whose prototype matches the one declared in `stdio.h`. 9 | 10 | 2. `WrappedLibc`'s `printf` calls `dlsym` with the module handle `RTLD_NEXT` to find the original `printf` at runtime and invoke it with its own arguments. It can't invoke it with the original arguments because `printf` is variadic. So it creates a `va_list` and calls `vprintf` instead. 11 | 12 | 3. `WrappedLibc`'s `OTHER_LDFLAGS` includes `-sub_library libSystem`, which makes `libWrappedLibc.dylib` reexport all the symbols from `libSystem` (to which `libc` is an alias on OS X). 13 | 14 | 4. `WrapperTester` links against `libWrappedLibc.dylib`. It does *not* link against `libSystem` --- `LINK_WITH_STANDARD_LIBRARIES` is set to `NO`. This causes it to look up all standard library functions as indirect symbols via `libWrappedLibc`. 15 | -------------------------------------------------------------------------------- /WrappedLibc/printf_wrapped.c: -------------------------------------------------------------------------------- 1 | // Wrapper 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | extern int printf(const char * restrict format, ...); 10 | extern int vprintf(const char * restrict format, va_list ap); 11 | 12 | int printf(const char * restrict format, ...) 13 | { 14 | static int (*real_printf)(const char * restrict, ...); 15 | if (!real_printf) { 16 | real_printf = dlsym(RTLD_NEXT, "printf"); 17 | if (!real_printf) { 18 | write(2, "dlsym: ", 7); 19 | 20 | const char *err = dlerror(); 21 | write(2, err, strlen(err)); 22 | 23 | write(2, "\n", 1); 24 | 25 | abort(); 26 | } 27 | } 28 | 29 | real_printf("wrapped printf:\n"); 30 | 31 | va_list ap; 32 | va_start(ap, format); 33 | int retval = vprintf(format, ap); 34 | va_end(ap); 35 | return retval; 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Kyle Sluder 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /RuntimeWrapping.xcodeproj/project.xcworkspace/xcshareddata/RuntimeWrapping.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | D157DAC6-7963-4473-83CA-A54BFF83841F 9 | IDESourceControlProjectName 10 | RuntimeWrapping 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 0C1F2243-83CA-4A6C-AA34-920A0A908485 14 | svn+ssh://source.omnigroup.com/Source/svn/Omni/trunk/Staff/kyle/Experiments 15 | 16 | IDESourceControlProjectPath 17 | RuntimeWrapping/RuntimeWrapping/RuntimeWrapping.xcodeproj/project.xcworkspace 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 0C1F2243-83CA-4A6C-AA34-920A0A908485 21 | ../../../.. 22 | 23 | IDESourceControlProjectRepositoryRootDictionary 24 | 25 | 0C1F2243-83CA-4A6C-AA34-920A0A908485 26 | svn+ssh://source.omnigroup.com/Source/svn/Omni 27 | 28 | IDESourceControlProjectURL 29 | svn+ssh://source.omnigroup.com/Source/svn/Omni/trunk/Staff/kyle/Experiments/RuntimeWrapping/RuntimeWrapping/RuntimeWrapping.xcodeproj 30 | IDESourceControlProjectVersion 31 | 110 32 | IDESourceControlProjectWCCIdentifier 33 | 0C1F2243-83CA-4A6C-AA34-920A0A908485 34 | IDESourceControlProjectWCConfigurations 35 | 36 | 37 | IDESourceControlRepositoryBranchesRelativeLocationKey 38 | branches 39 | IDESourceControlRepositoryExtensionIdentifierKey 40 | public.vcs.subversion 41 | IDESourceControlRepositoryTagsRelativeLocationKey 42 | tags 43 | IDESourceControlRepositoryTrunkRelativeLocationKey 44 | trunk 45 | IDESourceControlWCCIdentifierKey 46 | 0C1F2243-83CA-4A6C-AA34-920A0A908485 47 | IDESourceControlWCCName 48 | Experiments 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /RuntimeWrapping.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 27832D2C184C6E8C00CB937A /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 27832D2B184C6E8C00CB937A /* main.c */; }; 11 | 27832D4B184C6FD800CB937A /* printf_wrapped.c in Sources */ = {isa = PBXBuildFile; fileRef = 27832D34184C6EF000CB937A /* printf_wrapped.c */; }; 12 | 27832D4C184C6FF400CB937A /* libWrappedLibc.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 27832D47184C6F6300CB937A /* libWrappedLibc.dylib */; }; 13 | /* End PBXBuildFile section */ 14 | 15 | /* Begin PBXCopyFilesBuildPhase section */ 16 | 27832D26184C6E8C00CB937A /* CopyFiles */ = { 17 | isa = PBXCopyFilesBuildPhase; 18 | buildActionMask = 2147483647; 19 | dstPath = /usr/share/man/man1/; 20 | dstSubfolderSpec = 0; 21 | files = ( 22 | ); 23 | runOnlyForDeploymentPostprocessing = 1; 24 | }; 25 | /* End PBXCopyFilesBuildPhase section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 27832D28184C6E8C00CB937A /* WrapperTester */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = WrapperTester; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 27832D2B184C6E8C00CB937A /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = ""; }; 30 | 27832D34184C6EF000CB937A /* printf_wrapped.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = printf_wrapped.c; sourceTree = ""; }; 31 | 27832D47184C6F6300CB937A /* libWrappedLibc.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libWrappedLibc.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | /* End PBXFileReference section */ 33 | 34 | /* Begin PBXFrameworksBuildPhase section */ 35 | 27832D25184C6E8C00CB937A /* Frameworks */ = { 36 | isa = PBXFrameworksBuildPhase; 37 | buildActionMask = 2147483647; 38 | files = ( 39 | 27832D4C184C6FF400CB937A /* libWrappedLibc.dylib in Frameworks */, 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | 27832D44184C6F6300CB937A /* Frameworks */ = { 44 | isa = PBXFrameworksBuildPhase; 45 | buildActionMask = 2147483647; 46 | files = ( 47 | ); 48 | runOnlyForDeploymentPostprocessing = 0; 49 | }; 50 | /* End PBXFrameworksBuildPhase section */ 51 | 52 | /* Begin PBXGroup section */ 53 | 27832D1F184C6E8C00CB937A = { 54 | isa = PBXGroup; 55 | children = ( 56 | 27832D2A184C6E8C00CB937A /* RuntimeWrapping */, 57 | 27832D3B184C6F4600CB937A /* WrappedLibc */, 58 | 27832D29184C6E8C00CB937A /* Products */, 59 | ); 60 | sourceTree = ""; 61 | }; 62 | 27832D29184C6E8C00CB937A /* Products */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | 27832D28184C6E8C00CB937A /* WrapperTester */, 66 | 27832D47184C6F6300CB937A /* libWrappedLibc.dylib */, 67 | ); 68 | name = Products; 69 | sourceTree = ""; 70 | }; 71 | 27832D2A184C6E8C00CB937A /* RuntimeWrapping */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 27832D2B184C6E8C00CB937A /* main.c */, 75 | ); 76 | path = RuntimeWrapping; 77 | sourceTree = ""; 78 | }; 79 | 27832D3B184C6F4600CB937A /* WrappedLibc */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | 27832D34184C6EF000CB937A /* printf_wrapped.c */, 83 | ); 84 | path = WrappedLibc; 85 | sourceTree = ""; 86 | }; 87 | /* End PBXGroup section */ 88 | 89 | /* Begin PBXHeadersBuildPhase section */ 90 | 27832D45184C6F6300CB937A /* Headers */ = { 91 | isa = PBXHeadersBuildPhase; 92 | buildActionMask = 2147483647; 93 | files = ( 94 | ); 95 | runOnlyForDeploymentPostprocessing = 0; 96 | }; 97 | /* End PBXHeadersBuildPhase section */ 98 | 99 | /* Begin PBXNativeTarget section */ 100 | 27832D27184C6E8C00CB937A /* WrapperTester */ = { 101 | isa = PBXNativeTarget; 102 | buildConfigurationList = 27832D31184C6E8C00CB937A /* Build configuration list for PBXNativeTarget "WrapperTester" */; 103 | buildPhases = ( 104 | 27832D24184C6E8C00CB937A /* Sources */, 105 | 27832D25184C6E8C00CB937A /* Frameworks */, 106 | 27832D26184C6E8C00CB937A /* CopyFiles */, 107 | ); 108 | buildRules = ( 109 | ); 110 | dependencies = ( 111 | ); 112 | name = WrapperTester; 113 | productName = RuntimeWrapping; 114 | productReference = 27832D28184C6E8C00CB937A /* WrapperTester */; 115 | productType = "com.apple.product-type.tool"; 116 | }; 117 | 27832D46184C6F6300CB937A /* WrappedLibc */ = { 118 | isa = PBXNativeTarget; 119 | buildConfigurationList = 27832D48184C6F6300CB937A /* Build configuration list for PBXNativeTarget "WrappedLibc" */; 120 | buildPhases = ( 121 | 27832D43184C6F6300CB937A /* Sources */, 122 | 27832D44184C6F6300CB937A /* Frameworks */, 123 | 27832D45184C6F6300CB937A /* Headers */, 124 | ); 125 | buildRules = ( 126 | ); 127 | dependencies = ( 128 | ); 129 | name = WrappedLibc; 130 | productName = WrappedLibc; 131 | productReference = 27832D47184C6F6300CB937A /* libWrappedLibc.dylib */; 132 | productType = "com.apple.product-type.library.dynamic"; 133 | }; 134 | /* End PBXNativeTarget section */ 135 | 136 | /* Begin PBXProject section */ 137 | 27832D20184C6E8C00CB937A /* Project object */ = { 138 | isa = PBXProject; 139 | attributes = { 140 | LastUpgradeCheck = 0500; 141 | ORGANIZATIONNAME = "Kyle Sluder"; 142 | }; 143 | buildConfigurationList = 27832D23184C6E8C00CB937A /* Build configuration list for PBXProject "RuntimeWrapping" */; 144 | compatibilityVersion = "Xcode 3.2"; 145 | developmentRegion = English; 146 | hasScannedForEncodings = 0; 147 | knownRegions = ( 148 | en, 149 | ); 150 | mainGroup = 27832D1F184C6E8C00CB937A; 151 | productRefGroup = 27832D29184C6E8C00CB937A /* Products */; 152 | projectDirPath = ""; 153 | projectRoot = ""; 154 | targets = ( 155 | 27832D27184C6E8C00CB937A /* WrapperTester */, 156 | 27832D46184C6F6300CB937A /* WrappedLibc */, 157 | ); 158 | }; 159 | /* End PBXProject section */ 160 | 161 | /* Begin PBXSourcesBuildPhase section */ 162 | 27832D24184C6E8C00CB937A /* Sources */ = { 163 | isa = PBXSourcesBuildPhase; 164 | buildActionMask = 2147483647; 165 | files = ( 166 | 27832D2C184C6E8C00CB937A /* main.c in Sources */, 167 | ); 168 | runOnlyForDeploymentPostprocessing = 0; 169 | }; 170 | 27832D43184C6F6300CB937A /* Sources */ = { 171 | isa = PBXSourcesBuildPhase; 172 | buildActionMask = 2147483647; 173 | files = ( 174 | 27832D4B184C6FD800CB937A /* printf_wrapped.c in Sources */, 175 | ); 176 | runOnlyForDeploymentPostprocessing = 0; 177 | }; 178 | /* End PBXSourcesBuildPhase section */ 179 | 180 | /* Begin XCBuildConfiguration section */ 181 | 27832D2F184C6E8C00CB937A /* Debug */ = { 182 | isa = XCBuildConfiguration; 183 | buildSettings = { 184 | ALWAYS_SEARCH_USER_PATHS = NO; 185 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 186 | CLANG_CXX_LIBRARY = "libc++"; 187 | CLANG_ENABLE_OBJC_ARC = YES; 188 | CLANG_WARN_BOOL_CONVERSION = YES; 189 | CLANG_WARN_CONSTANT_CONVERSION = YES; 190 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 191 | CLANG_WARN_EMPTY_BODY = YES; 192 | CLANG_WARN_ENUM_CONVERSION = YES; 193 | CLANG_WARN_INT_CONVERSION = YES; 194 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 195 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 196 | COPY_PHASE_STRIP = NO; 197 | GCC_C_LANGUAGE_STANDARD = gnu99; 198 | GCC_DYNAMIC_NO_PIC = NO; 199 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 200 | GCC_OPTIMIZATION_LEVEL = 0; 201 | GCC_PREPROCESSOR_DEFINITIONS = ( 202 | "DEBUG=1", 203 | "$(inherited)", 204 | ); 205 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 206 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 207 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 208 | GCC_WARN_UNDECLARED_SELECTOR = YES; 209 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 210 | GCC_WARN_UNUSED_FUNCTION = YES; 211 | GCC_WARN_UNUSED_VARIABLE = YES; 212 | MACOSX_DEPLOYMENT_TARGET = 10.9; 213 | ONLY_ACTIVE_ARCH = YES; 214 | SDKROOT = macosx; 215 | }; 216 | name = Debug; 217 | }; 218 | 27832D30184C6E8C00CB937A /* Release */ = { 219 | isa = XCBuildConfiguration; 220 | buildSettings = { 221 | ALWAYS_SEARCH_USER_PATHS = NO; 222 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 223 | CLANG_CXX_LIBRARY = "libc++"; 224 | CLANG_ENABLE_OBJC_ARC = YES; 225 | CLANG_WARN_BOOL_CONVERSION = YES; 226 | CLANG_WARN_CONSTANT_CONVERSION = YES; 227 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 228 | CLANG_WARN_EMPTY_BODY = YES; 229 | CLANG_WARN_ENUM_CONVERSION = YES; 230 | CLANG_WARN_INT_CONVERSION = YES; 231 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 232 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 233 | COPY_PHASE_STRIP = YES; 234 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 235 | ENABLE_NS_ASSERTIONS = NO; 236 | GCC_C_LANGUAGE_STANDARD = gnu99; 237 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 238 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 239 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 240 | GCC_WARN_UNDECLARED_SELECTOR = YES; 241 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 242 | GCC_WARN_UNUSED_FUNCTION = YES; 243 | GCC_WARN_UNUSED_VARIABLE = YES; 244 | MACOSX_DEPLOYMENT_TARGET = 10.9; 245 | SDKROOT = macosx; 246 | }; 247 | name = Release; 248 | }; 249 | 27832D32184C6E8C00CB937A /* Debug */ = { 250 | isa = XCBuildConfiguration; 251 | buildSettings = { 252 | LINK_WITH_STANDARD_LIBRARIES = NO; 253 | PRODUCT_NAME = "$(TARGET_NAME)"; 254 | }; 255 | name = Debug; 256 | }; 257 | 27832D33184C6E8C00CB937A /* Release */ = { 258 | isa = XCBuildConfiguration; 259 | buildSettings = { 260 | LINK_WITH_STANDARD_LIBRARIES = NO; 261 | PRODUCT_NAME = "$(TARGET_NAME)"; 262 | }; 263 | name = Release; 264 | }; 265 | 27832D49184C6F6300CB937A /* Debug */ = { 266 | isa = XCBuildConfiguration; 267 | buildSettings = { 268 | EXECUTABLE_PREFIX = lib; 269 | GCC_PREPROCESSOR_DEFINITIONS = ( 270 | "DEBUG=1", 271 | "$(inherited)", 272 | ); 273 | OTHER_LDFLAGS = ( 274 | "-sub_library", 275 | libSystem, 276 | ); 277 | PRODUCT_NAME = "$(TARGET_NAME)"; 278 | }; 279 | name = Debug; 280 | }; 281 | 27832D4A184C6F6300CB937A /* Release */ = { 282 | isa = XCBuildConfiguration; 283 | buildSettings = { 284 | EXECUTABLE_PREFIX = lib; 285 | OTHER_LDFLAGS = ( 286 | "-sub_library", 287 | libSystem, 288 | ); 289 | PRODUCT_NAME = "$(TARGET_NAME)"; 290 | }; 291 | name = Release; 292 | }; 293 | /* End XCBuildConfiguration section */ 294 | 295 | /* Begin XCConfigurationList section */ 296 | 27832D23184C6E8C00CB937A /* Build configuration list for PBXProject "RuntimeWrapping" */ = { 297 | isa = XCConfigurationList; 298 | buildConfigurations = ( 299 | 27832D2F184C6E8C00CB937A /* Debug */, 300 | 27832D30184C6E8C00CB937A /* Release */, 301 | ); 302 | defaultConfigurationIsVisible = 0; 303 | defaultConfigurationName = Release; 304 | }; 305 | 27832D31184C6E8C00CB937A /* Build configuration list for PBXNativeTarget "WrapperTester" */ = { 306 | isa = XCConfigurationList; 307 | buildConfigurations = ( 308 | 27832D32184C6E8C00CB937A /* Debug */, 309 | 27832D33184C6E8C00CB937A /* Release */, 310 | ); 311 | defaultConfigurationIsVisible = 0; 312 | defaultConfigurationName = Release; 313 | }; 314 | 27832D48184C6F6300CB937A /* Build configuration list for PBXNativeTarget "WrappedLibc" */ = { 315 | isa = XCConfigurationList; 316 | buildConfigurations = ( 317 | 27832D49184C6F6300CB937A /* Debug */, 318 | 27832D4A184C6F6300CB937A /* Release */, 319 | ); 320 | defaultConfigurationIsVisible = 0; 321 | defaultConfigurationName = Release; 322 | }; 323 | /* End XCConfigurationList section */ 324 | }; 325 | rootObject = 27832D20184C6E8C00CB937A /* Project object */; 326 | } 327 | --------------------------------------------------------------------------------