A few questions

Artur Skawina art.08.09 at gmail.com
Fri Jul 27 13:33:54 PDT 2012


On 07/27/12 21:48, Adam D. Ruppe wrote:
> On Windows, an access violation (from a null pointer or other
> causes) is an exception that is thrown and can even be caught.
> 
> On Linux, a segfault is a signal that just kills the program,
> it doesn't work like a regular exception.

It's not a regular D exception, but it is a signal that /can/
be caught and used to print stacktraces, file names, line numbers
etc, not to mention you optionally get a snapshot of the program
as it failed (the "core" file).

The only non-trivial part is getting at the debug info to map the
addresses to symbols. Simple quick and dirty example below, which
will not only print the address of the instruction that caused the
fault, but also the address that it tried to access, and may even
sometimes succeed in letting the program continue to run.
Making it work with a non-gdc compiler, non-x86 ISA, hooking up w/
a library to get all the symbol names and properly handling all the
required cases is left as an exercise for the reader. :)

But, no, special handling for null dereferencing in the language is
*not* needed. Some more runtime support, maybe.

artur

   import std.stdio;

   template Seq(alias A, alias B, S...) {
      static if(S.length==0)
         alias Seq!(A, B, A) Seq;
      else static if (S[$-1]!=B)
         alias Seq!(A, B, S, S[$-1]+1) Seq;
      else
         alias S Seq;
   }

   struct hack {
      import core.sys.posix.ucontext, core.sys.posix.signal;
      alias int c_int;

      static:
      void* av;
      ubyte* violator;
      void*[4] trace;

      extern (C) void handler(c_int signum, siginfo_t* si, void* _ctx ) {
         auto ctx = cast(ucontext_t*)_ctx;
         av = si._sifields._sigfault.si_addr;
         version (X86) enum REG_RIP = 14;
         violator = cast(ubyte*)ctx.uc_mcontext.gregs[REG_RIP];
         ctx.uc_mcontext.gregs[REG_RIP] += inslen(violator);
         // scan and store backtrace etc.
         version (GNU) {
            import gcc.builtins;
            foreach (uint i; Seq!(0, trace.length-1))
               trace[i] = __builtin_return_address(i);
         }
         checkav(); // Not something you wanna do from a signal handler...
      }

      void register() {
         sigaction_t sa;
         sa.sa_sigaction = &handler;
         sa.sa_flags = SA_SIGINFO;
         if (sigaction(SIGSEGV, &sa, null))
            throw new Error("sigaction failed");
      }

      version (X86) size_t inslen(in ubyte* c) {
         if (c[0]==0xc6 && c[1]==0x05) return 7;
         if (c[0]==0x0f && c[1]==0xb6 && c[2]==0x4b) return 4;
         if (c[0]==0x0f && c[1]==0xb6 && c[2]==0x43) return 4;
         if (c[0]==0x0f && c[1]==0xb6) return 7;
         if (c[0]==0xa2) return 5;
         if (c[0]==0x65 && c[1]==0xc7) return 11;
         if (c[0]==0x88 && c[1]==0x4b) return 3;
         // ...
         return 1;
      }

      auto checkav() {
         if (av) {
            writefln("    0x%08x tried to access 0x%08x", violator, av);
            foreach(t; trace)
               writef("    0x%08x\n", t);
            av = null;
         }
      }
   }

   __gshared ubyte* p = null;

   int main() {
      hack.register();
      p[1] = 1; hack.checkav();
      p[2] = 2; hack.checkav();
      p[3] = 3; hack.checkav();
      p[5] = p[4]; hack.checkav();
      return p[42];
   }




More information about the Digitalmars-d-learn mailing list