1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#![macro_use]
//! Registers a function to be called before main (if an executable) or when loaded (if a dynamic
//! library).
//! **Using this is a bad idea.** You can do this in a way that isn't abysmally fragile.
//! 
//! Example
//! =======
//! 
//! ```
//! # #[macro_use] extern crate constructor;
//! pub static mut X: usize = 0;
//! 
//! extern fn init() {
//!     unsafe { X = 5; }
//! }
//! constructor! { init }
//! 
//! 
//! fn main() {
//!    assert_eq!(unsafe { X }, 5);
//! }
//! ```
//! 
//! Caveats
//! =======
//! This isn't exactly portable, though the implementation is quite simple.
//! 
//! Doing anything particularly complicated, such IO or loading libraries, may cause problems
//! on Windows. (?)
//! 
//! Every parent module must be `pub`lic. If it is not, then it will be
//! stripped out by `--release`. At least the compiler gives a helpful warning.
//! 
//! 
//! 
//! Beware, for some say that these techniques can unleash a terrible evil.
//! [lazy_static](https://crates.io/crates/lazy_static) may be a more appropriate tool.

#[macro_export]
macro_rules! constructor {
    ($($FN:ident),*) => {
        $(pub mod $FN {
            #![allow(non_snake_case)]
            #![allow(dead_code)]
            #![allow(non_upper_case_globals)]
            #![deny(private_no_mangle_statics /* >>> constructor must be used from a pub mod <<< */)]
            // (The comment above is to make the error message more meaningful.)

            // http://stackoverflow.com/questions/35428834/how-to-specify-which-elf-section-to-use-in-a-rust-dylib
            // https://msdn.microsoft.com/en-us/library/bb918180.aspx
            // Help given by WindowsBunny!

            #[cfg(target_os = "linux")]
            #[link_section = ".ctors"]
            #[no_mangle]
            pub static $FN: extern fn() = super::$FN;

            // FIXME: macos untested
            #[cfg(target_os = "macos")]
            #[link_section = "__DATA,__mod_init_func"]
            #[no_mangle]
            pub static $FN: extern fn() = super::$FN;

            // FIXME: windows untested; may require something more complicated for certain target
            // triples?
            #[cfg(target_os = "windows")]
            #[link_section = ".CRT$XCU"]
            #[no_mangle]
            pub static $FN: extern fn() = super::$FN;

            // We could also just ignore cfg(target_os) & have 1 item, but this way we'll have a compilation error if we don't know `target_os`.
        })*
    };
}

#[cfg(test)]
pub mod test {
    static mut BUNNY: &'static str = "i'm just a cute li'l bunny!\nand i wont hurt nobody!!\n🐰";

    #[test]
    fn bunny_is_fluffy() {
        println!("{}", unsafe { BUNNY });
    }



    pub static mut RAN: bool = false;
    extern "C" fn set_ran() {
        unsafe { RAN = true }
    }
    constructor! { set_ran }

    #[test]
    fn works() {
        assert!(unsafe { RAN });
    }



    extern crate zalgo;
    constructor! { corrupt_bunny }
    pub extern "C" fn corrupt_bunny() {
        unsafe {
            use self::zalgo::*;
            let mr_bun = gen(BUNNY, false, false, true, ZalgoSize::None);
            let red = "\x1b[31m";
            let reset = "\x1b[0m";
            let mr_bun = format!("{}{}{}", red, mr_bun, reset);

            use std::mem::{transmute, forget};
            {
                let mr_bun: &str = &mr_bun;
                BUNNY = transmute(mr_bun);
            }
            forget(mr_bun); // u din't see nuffin'
        }
        // ... okay there's probably a terminal somewhere that does a decent job of this.
    }
}