Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

usdt_probe_args on i386 should leave %ebx alone #12

Closed
melloc opened this issue Oct 18, 2016 · 0 comments
Closed

usdt_probe_args on i386 should leave %ebx alone #12

melloc opened this issue Oct 18, 2016 · 0 comments
Assignees

Comments

@melloc
Copy link
Collaborator

melloc commented Oct 18, 2016

The i386 code for building up the probe arguments stores the probe address in %ebx, a callee-saved register. Since it doesn't save and restore the initial value, any code that calls into it and expects %ebx to be the same value when it returns will have an issue. I found this while looking into some test failures on Mac OS X.

Using the following D program:

typedef struct usdt_probe {
        int (*isenabled_addr)(void);
        void *probe_addr;
} usdt_probe_t;

pid$target:DTraceProviderBindings.node:*_fire*:
{
    printf("%%eax = %x, %%ebx = %x, %%ecx = %x\n", uregs[R_EAX], uregs[R_EBX], uregs[R_ECX]);
}

testlibusdt*::: { printf("%d", arg0); }

proc:::exit {}

pid$target::valloc:return
{
    printf("%p\n", arg1);
}

pid$target:DTraceProviderBindings.node:usdt_is_enabled:entry
{
    self->enabled_struct = (usdt_probe_t *)copyin(arg0, sizeof (usdt_probe_t));
    self->enabled_addr = (uint32_t)self->enabled_struct->isenabled_addr;
}

pid$target:DTraceProviderBindings.node:usdt_is_enabled:entry
{
    print(*self->enabled_struct);
    printf("%x\n", self->enabled_addr);
}

pid$target:DTraceProviderBindings.node:usdt_is_enabled:entry
{
    tracemem(copyin(self->enabled_addr, 32), 32);
}

I was able to track the value of %ebx throughout the call to node-dtrace-provider's _fire:

% sudo dtrace -arch i386 -Z -s ../foo.d -c 'node test/32probe_fire.js'
dtrace: script '../foo.d' matched 1 probe
66305
dtrace: pid 66305: 'dtrace' is not a valid provider
dtrace: pid 66305 has exited
CPU     ID                    FUNCTION:NAME
  0  10988                    valloc:return 3802000
  0  10988                    valloc:return 3804000
...
  0  12500 node::DTraceProbe::_fire(Nan::FunctionCallbackInfo<v8::Value> const&, int):38c %eax = 20, %ebx = 17001a0, %ecx = 1700150
  0  12501 node::DTraceProbe::_fire(Nan::FunctionCallbackInfo<v8::Value> const&, int):390 %eax = 20, %ebx = 17001a0, %ecx = 1700150
  0  12502 node::DTraceProbe::_fire(Nan::FunctionCallbackInfo<v8::Value> const&, int):394 %eax = 20, %ebx = 17001a0, %ecx = 1700150
  0  12503 node::DTraceProbe::_fire(Nan::FunctionCallbackInfo<v8::Value> const&, int):397 %eax = 20, %ebx = 17001a0, %ecx = 1700150
  0  12572            usdt_fire_probe:entry %eax = 20, %ebx = 17001a0, %ecx = 1700150
  0  12573                usdt_fire_probe:0 %eax = 20, %ebx = 17001a0, %ecx = 1700150
  0  12574                usdt_fire_probe:1 %eax = 20, %ebx = 17001a0, %ecx = 1700150
  0  12575                usdt_fire_probe:3 %eax = 20, %ebx = 17001a0, %ecx = 1700150
  0  12576                usdt_fire_probe:6 %eax = 20, %ebx = 17001a0, %ecx = 1700150
  0  12577                usdt_fire_probe:9 %eax = 1700150, %ebx = 17001a0, %ecx = 1700150
  0  12578                usdt_fire_probe:b %eax = 1700150, %ebx = 17001a0, %ecx = 1700150
  0  12579                usdt_fire_probe:d %eax = 1700150, %ebx = 17001a0, %ecx = 1700150
  0  12580               usdt_fire_probe:10 %eax = 1700150, %ebx = 17001a0, %ecx = bffff5c8
  0  12581               usdt_fire_probe:13 %eax = 1700150, %ebx = 17001a0, %ecx = bffff5c8
  0  12582               usdt_fire_probe:16 %eax = 380200c, %ebx = 17001a0, %ecx = bffff5c8
  0  12583               usdt_fire_probe:1a %eax = 380200c, %ebx = 17001a0, %ecx = bffff5c8
  0  12584               usdt_fire_probe:1e %eax = 380200c, %ebx = 17001a0, %ecx = bffff5c8
  0  12585               usdt_fire_probe:21 %eax = 380200c, %ebx = 17001a0, %ecx = bffff5c8
  0  12591                  32probe:32probe 10
  0  12586               usdt_fire_probe:26 %eax = bffff5c8, %ebx = 380200c, %ecx = 0
  0  12587               usdt_fire_probe:29 %eax = bffff5c8, %ebx = 380200c, %ecx = 0
  0  12588               usdt_fire_probe:2a %eax = bffff5c8, %ebx = 380200c, %ecx = 0
  0  12571           usdt_fire_probe:return %eax = bffff5c8, %ebx = 380200c, %ecx = 0
  0  12504 node::DTraceProbe::_fire(Nan::FunctionCallbackInfo<v8::Value> const&, int):39c %eax = bffff5c8, %ebx = 380200c, %ecx = 0
  0  12505 node::DTraceProbe::_fire(Nan::FunctionCallbackInfo<v8::Value> const&, int):3a0 %eax = bffff5c8, %ebx = 380200c, %ecx = 0
  0  12506 node::DTraceProbe::_fire(Nan::FunctionCallbackInfo<v8::Value> const&, int):3a2 %eax = bffff5c8, %ebx = 380200c, %ecx = 0
  0  12507 node::DTraceProbe::_fire(Nan::FunctionCallbackInfo<v8::Value> const&, int):3a4 %eax = bffff5c8, %ebx = 380200c, %ecx = 0
  0  12508 node::DTraceProbe::_fire(Nan::FunctionCallbackInfo<v8::Value> const&, int):3b0 %eax = bffff5c8, %ebx = 380200c, %ecx = 0

The program then segfaults at that last instruction (000009e4 below), which attempts to use a value calculated from %ebx (done at 000009e0):

% otool -tV build/Release/obj.target/DTraceProviderBindings/dtrace_probe.o
...
000009c7        calll   _usdt_fire_probe
000009cc        cmpl    $0x0, 0x10(%ebx)
000009d0        je      0x9fd
000009d2        xorl    %esi, %esi
000009d4        nopw    %cs:__ZN4node11DTraceProbeC2Ev(%eax,%eax) ## node::DTraceProbe::DTraceProbe()
000009e0        movl    0x14(%ebx,%esi,4), %eax
000009e4        movl    __ZN4node11DTraceProbeC2Ev(%eax), %ecx ## node::DTraceProbe::DTraceProbe()
000009e6        movl    -0x90(%ebp,%esi,4), %edx
000009ed        movl    %edx, 0x4(%esp)
000009f1        movl    %eax, __ZN4node11DTraceProbeC2Ev(%esp) ## node::DTraceProbe::DTraceProbe()
000009f4        calll   *0x8(%ecx)
000009f7        incl    %esi
000009f8        cmpl    0x10(%ebx), %esi
000009fb        jb      0x9e0
...

Instead of using %ebx, usdt_probe_args can use %edx, one of the x86 scratch registers, which must be caller-saved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant