├── README.md ├── Makefile ├── clang_wrap.rb ├── stack_ptr.c └── clearstack.cc /README.md: -------------------------------------------------------------------------------- 1 | clearstack 2 | 3 | This is an experimental clang plugin. If you compiles your program 4 | with this plugin, pointer values and integer values whose size is 5 | equal to pointers will be zeroed before a function exits. If this 6 | plugin is used to compile a program which uses conservative GC, 7 | you might be able to reduce the chance to have a false reference which 8 | prevents objects from being freed. 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS=-O -g -fPIC -std=c++11 -Wall 2 | CXXFLAGS+=`llvm-config-3.2 --cflags` 3 | #CXXFLAGS+=-I/home/i/src/llvm/include 4 | 5 | all: clearstack.so stack_ptr stack_ptr_cs 6 | 7 | clearstack.so: clearstack.o 8 | $(CXX) -shared $< -o $@ 9 | 10 | stack_ptr: stack_ptr.c 11 | $(CC) -g $< -o $@ -lgc -O2 12 | 13 | stack_ptr_cs: stack_ptr.c clearstack.so 14 | ./clang_wrap.rb $< -c -o stack_ptr_cs.o -g && gcc stack_ptr_cs.o -o $@ -lgc -O2 15 | 16 | clean: 17 | rm -f clearstack.so *.o 18 | -------------------------------------------------------------------------------- /clang_wrap.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | CLANG='clang' 4 | OPT='opt-3.2' 5 | LLC='llc-3.2' 6 | 7 | def my_system(*a) 8 | p a 9 | r = system(*a) 10 | if !r 11 | exit(1) 12 | end 13 | end 14 | 15 | use_plugin = false 16 | 17 | argv = ARGV.dup 18 | i = nil 19 | # Clang crashes for Ruby's bignum.c. I don't know why. 20 | if (i = argv.index('-c')) && (j = argv.index('-o')) && !argv.index('bignum.c') 21 | argv[i] = '-S' 22 | ot = argv[j+1] 23 | argv[j+1] += '.s' 24 | argv.push('-emit-llvm') 25 | use_plugin = true 26 | end 27 | 28 | my_system(CLANG, *argv) 29 | 30 | if use_plugin 31 | bc = argv[j+1] + '.bc' 32 | my_system(OPT, '-load=' + File.dirname(__FILE__) + '/clearstack.so', 33 | '-clearstack', argv[j+1], '-o', bc) 34 | my_system(LLC, bc, '-o', bc + '.s', '-relocation-model=pic') 35 | my_system(CLANG, bc + '.s', '-c', '-o', ot) 36 | end 37 | -------------------------------------------------------------------------------- /stack_ptr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void alloc() { 6 | void* p = GC_malloc(3); 7 | // Let the pointer be on the stack. 8 | asm(""::"m"(p)); 9 | } 10 | 11 | void main_loop(void* p) { 12 | size_t before_gc = GC_get_free_bytes(); 13 | GC_gcollect(); 14 | size_t after_gc = GC_get_free_bytes(); 15 | printf("%s: %zu => %zu\n", 16 | before_gc < after_gc ? "reclaimed" : "not reclaimed", 17 | before_gc, after_gc); 18 | } 19 | 20 | void call_main_loop() { 21 | // The stack location used in alloc() is not used in this function. 22 | void* p; 23 | // The allocated memory will be properly reclaimed even without our 24 | // clang plugin if we have this line. 25 | // p = NULL; 26 | asm(""::"m"(p)); 27 | main_loop(p); 28 | } 29 | 30 | int main() { 31 | GC_init(); 32 | GC_gcollect(); 33 | 34 | alloc(); 35 | call_main_loop(); 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /clearstack.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define VERBOSE 13 | #if defined(VERBOSE) 14 | #define LOG(...) printf(__VA_ARGS__) 15 | #else 16 | #define LOG(...) 17 | #endif 18 | 19 | #if defined(__x86_64__) 20 | #define PTR_BITS 64 21 | #else 22 | #define PTR_BITS 32 23 | #endif 24 | 25 | using namespace llvm; 26 | using namespace std; 27 | 28 | class ClearStack : public FunctionPass { 29 | public: 30 | ClearStack() : FunctionPass(ID) {} 31 | 32 | virtual bool runOnFunction(Function& func) { 33 | LOG("func %s\n", func.getName().data()); 34 | vector allocas; 35 | for (BasicBlock& bb : func) { 36 | for (Instruction& inst : bb) { 37 | if (inst.getOpcode() == Instruction::Alloca) { 38 | AllocaInst& alloca = dynamic_cast(inst); 39 | if (!alloca.isStaticAlloca()) 40 | continue; 41 | 42 | Type* type = alloca.getAllocatedType(); 43 | LOG("%u %s %d align=%u int=%d arr=%d prim=%u sca=%u %s\n", 44 | alloca.getOpcode(), alloca.getOpcodeName(), 45 | alloca.isStaticAlloca(), alloca.getAlignment(), 46 | type->isIntegerTy(), 47 | type->isArrayTy(), 48 | type->getPrimitiveSizeInBits(), 49 | type->getScalarSizeInBits(), 50 | alloca.getName().data()); 51 | if ((type->isIntegerTy() && 52 | type->getPrimitiveSizeInBits() == PTR_BITS) || 53 | type->isPointerTy()) 54 | allocas.push_back(&alloca); 55 | } else if (inst.getOpcode() == Instruction::Ret) { 56 | ReturnInst& ret = dynamic_cast(inst); 57 | LOG("ret: %zu\n", allocas.size()); 58 | for (AllocaInst* alloca : allocas) { 59 | Constant* zero; 60 | if (alloca->getAllocatedType()->isIntegerTy()) 61 | zero = ConstantInt::get(alloca->getAllocatedType(), 0); 62 | else 63 | zero = ConstantPointerNull::get( 64 | static_cast(alloca->getAllocatedType())); 65 | LOG("fill zero %p => %s\n", zero, alloca->getName().data()); 66 | new StoreInst(zero, alloca, true, &ret); 67 | } 68 | } 69 | } 70 | } 71 | return false; 72 | } 73 | 74 | static char ID; 75 | }; 76 | 77 | char ClearStack::ID = 'a'; 78 | static RegisterPass g_cs( 79 | "clearstack", "clear stack", false, false); 80 | --------------------------------------------------------------------------------