On Tue, Jul 04, 2017 at 06:26:22PM +0100, Daniel P. Berrange wrote:
On Tue, Jul 04, 2017 at 05:32:03PM +0100, Daniel P. Berrange wrote:
> On Tue, Jul 04, 2017 at 12:21:21PM +0100, Daniel P. Berrange wrote:
> So I did some debugging and this is wierder than I can imagine possible.
>
> I put a printf statement in virNumaGetMaxNode in qemuxml2argvmock.c
> to print out the return value.
>
> I also put a printf statement in virNumaNodeIsAvailable in virnuma.c
> (in the non-NUMACTL conditional block), that prints out the return
> value received from virNumaGetMaxNode
>
> The first printf displays 7, while the second printf displays -1
>
> So we're definitely calling our mock override, but the return
> value is getting mangled when seen by the caller, which is a
> giant wtf to me.
I wrote an isolated test case
$ cat lib.h
int get_max(void) __attribute__((noinline));
int is_ok(int i);
$ cat lib.c
#include <stdio.h>
#include "lib.h"
int get_max(void)
{
fprintf(stderr, "In original max, returning 3\n");
return 3;
}
int is_ok(int i)
{
int max = get_max();
fprintf(stderr, "Received max %d\n", max);
return i > 0 && i <= max;
}
$ cat mock.c
#include <stdio.h>
#include "lib.h"
int get_max(void)
{
fprintf(stderr, "In mock max, returning 7\n");
return 7;
}
$ cat run.c
#include <stdio.h>
#include "lib.h"
int main(int argc, char **argv)
{
fprintf(stderr, "Is 5 in range ? %d\n", is_ok(5));
}
$ clang -O2 -g -Wall -shared -o libdemo.so -fPIC lib.c
$ clang -O2 -g -Wall -shared -o mock.so -fPIC mock.c
$ clang -O2 -Wall -o run run.c -L. -ldemo
$ ./run
In original max, returning 3
Received max 3
Is 5 in range ? 0
$ LD_PRELOAD=mock.so ./run
In mock max, returning 7
Received max 3
Is 5 in range ? 0
$ clang -O0 -g -Wall -shared -o libdemo.so -fPIC lib.c
$ LD_PRELOAD=mock.so ./run
In mock max, returning 7
Received max 7
Is 5 in range ? 1
So clang is definitely *not* inlining the function, *is* running out mock
function, but none the less getting the return value from the original
function.
Turning on optimizer debugging i see
$ clang -O2 -g -Wall -shared -o libdemo.so -Rpass=.* -fPIC lib.c
lib.c:7:3: remark: marked this call a tail call candidate [-Rpass=tailcallelim]
fprintf(stderr, "In original max, returning 3\n");
^
lib.c:11:15: remark: marked this call a tail call candidate [-Rpass=tailcallelim]
int is_ok(int i)
^
lib.c:13:13: remark: marked this call a tail call candidate [-Rpass=tailcallelim]
int max = get_max();
^
lib.c:13:7: remark: marked this call a tail call candidate [-Rpass=tailcallelim]
int max = get_max();
^
lib.c:14:3: remark: marked this call a tail call candidate [-Rpass=tailcallelim]
fprintf(stderr, "Received max %d\n", max);
^
so, I'm thinking this problem is a result of tail call optimization making
an assumption that is violated when mocking the function.
The tail call stuff was a red-herring and not related. After much trial
and error I've found that it is possible to make this work by annotating
the functions with the attribute "weak". This explicitly tells the
compiler that the function is designed to be overridden by an external
source, thus preventing any of the call convention optimization clang
does. With 'weak' added to virNumaGetMaxNode() the test suite passes
on FreeBSD !
Regards,
Daniel
--
|:
https://berrange.com -o-
https://www.flickr.com/photos/dberrange :|
|:
https://libvirt.org -o-
https://fstop138.berrange.com :|
|:
https://entangle-photo.org -o-
https://www.instagram.com/dberrange :|