summaryrefslogtreecommitdiffstats
path: root/libdmitry/libdmitry.c
blob: 64722631a677a390bf70863c776ed7f1b2464403 (plain)
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#define LOG_TAG "libdmitry"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <pthread.h>
#include <utils/Log.h>
#include <hardware/power.h>
#include <hardware/hardware.h>

/*
 * CURIOUS WHAT THE HELL IS GOING ON IN HERE? READ UP...
 *
 *
 * Problems:
 * 1. Nexus 10's GPS library was made to work with android L
 * 2. Android M changed a few things around that make it not work
 *   a. Sensor manager API changed in a few places
 *   b. BoringSSL replaced OpenSSL
 * 3. Due to these now-missing unresolved symbols GPS library will not load or run
 *
 * Curious data snippets
 *  Due to peculiarities of the ELF format, when a binary baz imports function foo() from libbar.so,
 *   nowhere in baz's ELF file does it say that foo() must from from libbar. In fact there are two
 *   separate records. One that says that libbar is "NEED"ed, and another that says that there is an
 *   import of function "foo". What that means is that if the process wer to also load libxyz, which
 *   also exported foo(), there is no way to be sure which foo() would get called. Why do we care?
 *   Well, consider out problems above. We need to provide functions and variables that existing
 *   libraries no longer do. How?
 *
 * A tricky but clever solution: INTERPOSITION library
 * 1. We'll edit the GPS library and replace one of its "NEED" record with one referencing a new library
 *    which we'll create. Need a library name? why not "lidmitry"?
 * 2. Make sure that lidmitry's NEED records include the library whose record we replaced in the GPS
 *    library, to make sure that the linker brings it in afterall and all symbols in it are found
 * 3. Implement libdmitry such that it provides the missing things and does them in such a way that the
 *    GPS library is happy.
 * 4. Complications exist:
 *   a. This would be impossible to do in C++, as the compiler would barf at us implementing random
 *      chunks of random classes we do not control. Luckily, the linker has no idea about C++, C, or other
 *      such things. C++ names get converted to special symbol names by the compiler, and thus the linker
 *      is neevr even aware of overloading or such things. This process is called mangling. So we'll just
 *      export the C++ functions we need with the proper mangled names, and code them in C. This means
 *      that we need to follow the proper calling conventions by hand, etc. With some dissasembling to see
 *      how GCC does it, we can duplicate it here, as I did.
 *   b. Not all missing things are functions. There are a few variables that need to be exported and are
 *      not present in M's code. We have to provide them. Luckily, just like with functions, as long as our
 *      mandled name matches, the linker will be happy to make the connection for us.
 *   c. Some cleanup may be needed on exit. Luckily, there is a way to register functions to be called
 *      upon library load and unload. I use that here to free some state that may be left over on exit.
 *
 * Result: GPS library works on M, with the help of libdmitry and a small binary patch to the GPS
 *         library itself (replacing one of the "NEED" records with a NEED record for "libdmitry"
 */







//various funcs we'll need to call, in their mangled form

    //android::String8::String8(char const*)
    extern void _ZN7android7String8C1EPKc(void **str8P, const char *str);

    //android::String8::~String8()
    extern void _ZN7android7String8D1Ev(void **str8P);

    //android::String16::String16(char const*)
    extern void _ZN7android8String16C1EPKc(void **str16P, const char *str);

    //android::String16::~String16()
    extern void _ZN7android8String16D1Ev(void **str16P);

    //android::SensorManager::~SensorManager()
    extern void _ZN7android13SensorManagerD1Ev(void *sensorMgr);

    //android::SensorManager::SensorManager(android::String16 const&)
    extern void _ZN7android13SensorManagerC1ERKNS_8String16E(void *sensorMgr, void **str16P);

    //android::SensorManager::createEventQueue(android::String8, int)
    extern void _ZN7android13SensorManager16createEventQueueENS_7String8Ei(void **retVal, void *sensorMgr, void **str8P, int mode);


//data exports we must provide for gps library to be happy

    /*
     * DATA:     android::Singleton<android::SensorManager>::sLock
     * USE:      INTERPOSE: a mutes that GPS lib will insist on accessing
     * NOTES:    In L, the sensor manager exposed this lock that callers
     *           actually locked & unlocked when accessing it. In M this
     *           is no longer the case, but we still must provide it for
     *           the GPS library to be happy. It will lock nothnhing, but
     *           as long as it is a real lock and pthread_mutex_* funcs
     *           work on it, the GPS library will be happy.
     */
    pthread_mutex_t _ZN7android9SingletonINS_13SensorManagerEE5sLockE = PTHREAD_MUTEX_INITIALIZER;

    /*
     * DATA:     android::Singleton<android::SensorManager>::sInstance
     * USE:      INTERPOSE: a singleton instance of SensorManager
     * NOTES:    In L, the sensor manager exposed this variable, as it was
     *           a singleton and one could just access this directly to get
     *           the current already-existing instance if it happened to
     *           already exist. If not one would create one and store it
     *           there. In M this is entirely different, but the GPS library
     *           does not know that. So we'll init it to NULL to signify that
     *           no current instance exists, let it create one, and store it
     *           here, and upon unloading we'll clean it up, if it is not
     *           NULL (which is what it would be if the GPS library itself
     *           did the cleanup).
     */
    void* _ZN7android9SingletonINS_13SensorManagerEE9sInstanceE = NULL;


//code exports we provide

    //android::SensorManager::SensorManager(void)
    void _ZN7android13SensorManagerC1Ev(void *sensorMgr);

    //android::SensorManager::createEventQueue(void)
    void _ZN7android13SensorManager16createEventQueueEv(void **retVal, void *sensorMgr);

    //this used to exist in OpenSLL, but does not in BoringSSL - for some reason GPS library uses it anyways
    void *CRYPTO_malloc(uint32_t sz, const char *file, uint32_t line);


//library on-load and on-unload handlers (to help us set things up and tear them down)
    void libEvtLoading(void) __attribute__((constructor));
    void libEvtUnloading(void) __attribute__((destructor));


/*
 * FUNCTION: android::SensorManager::SensorManager(void)
 * USE:      INTERPOSE: construct a sensor manager object
 * NOTES:    This constructor no longer exists in M, instead now one must pass
 *           in a package name as a "string16" to the consrtuctor. Since this
 *           lib only services GPS library, it is easy for us to just do that
 *           and this provide the constructor that the GPS library wants.
 *           The package name we use if "gps.default". Why not?
 */
void _ZN7android13SensorManagerC1Ev(void *sensorMgr)
{
    void *string;

    _ZN7android8String16C1EPKc(&string, "gps.default");
    _ZN7android13SensorManagerC1ERKNS_8String16E(sensorMgr, &string);
    _ZN7android8String16D1Ev(&string);
}

/*
 * FUNCTION: android::SensorManager::createEventQueue(void)
 * USE:      INTERPOSE: create an event queue to receive events
 * NOTES:    This function no longer exists in M, instead now one must pass
 *           in a client name as a "string8" and an integer "mode"to it. M
 *           sources list default values for these params as an empty string
 *           and 0. So we'll craft the same call here.
 */
void _ZN7android13SensorManager16createEventQueueEv(void **retVal, void *sensorMgr)
{
    void *string;

    _ZN7android7String8C1EPKc(&string, "");
    _ZN7android13SensorManager16createEventQueueENS_7String8Ei(retVal, sensorMgr, &string, 0);
    _ZN7android7String8D1Ev(&string);
}

/*
 * FUNCTION: CRYPTO_malloc(uint32_t sz, const char *file, uint32_t line)
 * USE:      INTERPOSE: Allocate memory
 * NOTES:    In OpenSSL, this just allocates memory and optionally tracks it.
 *           Why manta's GPS library chose to use it is a mystery, but to make
 *           it happy we must provide it, so we do, backing the allocation with
 *           a calloc()-ed memory chunk.
 */
void *CRYPTO_malloc(uint32_t sz, const char *file, uint32_t line)
{
    (void)file;
    (void)line;
    return calloc(sz, 1);
}

/*
 * FUNCTION: libEvtLoading()
 * USE:      Handle library loading
 * NOTES:    This is a good time to log the fact that we were loaded and plan to
 *           do our thing.
 */
void libEvtLoading(void)
{
    ALOGI("Nexus 10 GPS interposition library loaded. Your GPS should work in M now.");
}

/*
 * FUNCTION: libEvtUnloading()
 * USE:      Handle library unloading
 * NOTES:    This is a good time to free whatever is unfreed and say goodbye
 */
void libEvtUnloading(void)
{
    ALOGI("Nexus 10 GPS interposition library unloading. Goodbye...");
    if (_ZN7android9SingletonINS_13SensorManagerEE9sInstanceE) {
        //if an instance stil exists, free it by calling the destructor, just to be throrough
        _ZN7android13SensorManagerD1Ev(_ZN7android9SingletonINS_13SensorManagerEE9sInstanceE);
        _ZN7android9SingletonINS_13SensorManagerEE9sInstanceE = NULL;
    }
}