summaryrefslogtreecommitdiff
path: root/opengl/libs/EGL/egl_layers.cpp
blob: ea86c9a05bd67bc1ee7e356f7d367988c85b8f72 (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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
/*
 ** Copyright 2018, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
 ** You may obtain a copy of the License at
 **
 **     http://www.apache.org/licenses/LICENSE-2.0
 **
 ** Unless required by applicable law or agreed to in writing, software
 ** distributed under the License is distributed on an "AS IS" BASIS,
 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 */

#include "egl_layers.h"

#include <EGL/egl.h>
#include <android-base/file.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android/dlext.h>
#include <dlfcn.h>
#include <graphicsenv/GraphicsEnv.h>
#include <log/log.h>
#include <nativebridge/native_bridge.h>
#include <nativeloader/native_loader.h>
#include <sys/prctl.h>

namespace android {

// GLES Layers
//
// - Layer discovery -
// 1. Check for debug layer list from GraphicsEnv
// 2. If none enabled, check system properties
//
// - Layer initializing -
// - AndroidGLESLayer_Initialize (provided by layer, called by loader)
// - AndroidGLESLayer_GetProcAddress (provided by layer, called by loader)
// - getNextLayerProcAddress (provided by loader, called by layer)
//
// 1. Walk through defs for egl and each gl version
// 2. Call GetLayerProcAddress passing the name and the target hook entry point
//   - This tells the layer the next point in the chain it should call
// 3. Replace the hook with the layer's entry point
//    - All entryoints will be present, anything unsupported by the driver will
//      have gl_unimplemented
//
// - Extension layering -
//  Not all functions are known to Android, so libEGL handles extensions.
//  They are looked up by applications using eglGetProcAddress
//  Layers can look them up with getNextLayerProcAddress

const int kFuncCount = sizeof(platform_impl_t) / sizeof(char*) + sizeof(egl_t) / sizeof(char*) +
        sizeof(gl_hooks_t) / sizeof(char*);

typedef struct FunctionTable {
    EGLFuncPointer x[kFuncCount];
    EGLFuncPointer& operator[](int i) { return x[i]; }
} FunctionTable;

// TODO: Move these to class
std::unordered_map<std::string, int> func_indices;
// func_indices.reserve(kFuncCount);

std::unordered_map<int, std::string> func_names;
// func_names.reserve(kFuncCount);

std::vector<FunctionTable> layer_functions;

const void* getNextLayerProcAddress(void* layer_id, const char* name) {
    // Use layer_id to find funcs for layer below current
    // This is the same key provided in AndroidGLESLayer_Initialize
    auto next_layer_funcs = reinterpret_cast<FunctionTable*>(layer_id);
    EGLFuncPointer val;

    ALOGV("getNextLayerProcAddress servicing %s", name);

    if (func_indices.find(name) == func_indices.end()) {
        // No entry for this function - it is an extension
        // call down the GPA chain directly to the impl
        ALOGV("getNextLayerProcAddress - name(%s) no func_indices entry found", name);

        // Look up which GPA we should use
        int gpaIndex = func_indices["eglGetProcAddress"];
        ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) <- using GPA from this index", name, gpaIndex);
        EGLFuncPointer gpaNext = (*next_layer_funcs)[gpaIndex];
        ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) <- using GPA at this address", name, gpaIndex, (unsigned long long)gpaNext);


        // Call it for the requested function
        typedef void* (*PFNEGLGETPROCADDRESSPROC)(const char*);
        PFNEGLGETPROCADDRESSPROC next = reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(gpaNext);

        val = reinterpret_cast<EGLFuncPointer>(next(name));
        ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) Got back (%llu) from GPA", name, gpaIndex, (unsigned long long)gpaNext, (unsigned long long)val);

        // We should store it now, but to do that, we need to move func_idx to the class so we can
        // increment it separately
        // TODO: Move func_idx to class and store the result of GPA
        return reinterpret_cast<void*>(val);
    }

    int index = func_indices[name];
    val = (*next_layer_funcs)[index];
    ALOGV("getNextLayerProcAddress - name(%s) index(%i) entry(%llu) - Got a hit, returning known entry", name, index, (unsigned long long)val);
    return reinterpret_cast<void*>(val);
}

void SetupFuncMaps(FunctionTable& functions, char const* const* entries, EGLFuncPointer* curr,
                   int& func_idx) {
    while (*entries) {
        const char* name = *entries;

        // Some names overlap, only fill with initial entry
        // This does mean that some indices will not be used
        if (func_indices.find(name) == func_indices.end()) {
            ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for func_indices, assigning now", name, func_idx);
            func_names[func_idx] = name;
            func_indices[name] = func_idx;
        } else {
            ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for func_indices", name, func_idx);
        }

        // Populate layer_functions once with initial value
        // These values will arrive in priority order, starting with platform entries
        if (functions[func_idx] == nullptr) {
            ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for functions, assigning (%llu)", name, func_idx, (unsigned long long) *curr);
            functions[func_idx] = *curr;
        } else {
            ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for functions (%llu)", name, func_idx, (unsigned long long) functions[func_idx]);
        }

        entries++;
        curr++;
        func_idx++;
    }
}

LayerLoader& LayerLoader::getInstance() {
    // This function is mutex protected in egl_init_drivers_locked and eglGetProcAddressImpl
    static LayerLoader layer_loader;

    if (!layer_loader.layers_loaded_) layer_loader.LoadLayers();

    return layer_loader;
}

const char kSystemLayerLibraryDir[] = "/data/local/debug/gles";

std::string LayerLoader::GetDebugLayers() {
    // Layers can be specified at the Java level in GraphicsEnvironment
    // gpu_debug_layers_gles = layer1:layer2:layerN
    std::string debug_layers = android::GraphicsEnv::getInstance().getDebugLayersGLES();

    if (debug_layers.empty()) {
        // Only check system properties if Java settings are empty
        debug_layers = base::GetProperty("debug.gles.layers", "");
    }

    return debug_layers;
}

EGLFuncPointer LayerLoader::ApplyLayer(layer_setup_func layer_setup, const char* name,
                                       EGLFuncPointer next) {
    // Walk through our list of LayerSetup functions (they will already be in reverse order) to
    // build up a call chain from the driver

    EGLFuncPointer layer_entry = next;

    layer_entry = layer_setup(name, layer_entry);

    if (next != layer_entry) {
        ALOGV("We succeeded, replacing hook (%llu) with layer entry (%llu), for %s",
              (unsigned long long)next, (unsigned long long)layer_entry, name);
    }

    return layer_entry;
}

EGLFuncPointer LayerLoader::ApplyLayers(const char* name, EGLFuncPointer next) {
    if (!layers_loaded_ || layer_setup_.empty()) return next;

    ALOGV("ApplyLayers called for %s with next (%llu), current_layer_ (%i)", name,
          (unsigned long long)next, current_layer_);

    EGLFuncPointer val = next;

    // Only ApplyLayers for layers that have been setup, not all layers yet
    for (unsigned i = 0; i < current_layer_; i++) {
        ALOGV("ApplyLayers: Calling ApplyLayer with i = %i for %s with next (%llu)", i, name,
              (unsigned long long)next);
        val = ApplyLayer(layer_setup_[i], name, val);
    }

    ALOGV("ApplyLayers returning %llu for %s", (unsigned long long)val, name);

    return val;
}

void LayerLoader::LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer* curr,
                                       char const* const* entries) {
    while (*entries) {
        char const* name = *entries;

        EGLFuncPointer prev = *curr;

        // Pass the existing entry point into the layer, replace the call with return value
        *curr = ApplyLayer(layer_setup, name, *curr);

        if (prev != *curr) {
            ALOGV("LayerPlatformEntries: Replaced (%llu) with platform entry (%llu), for %s",
                  (unsigned long long)prev, (unsigned long long)*curr, name);
        } else {
            ALOGV("LayerPlatformEntries: No change(%llu) for %s, which means layer did not "
                  "intercept",
                  (unsigned long long)prev, name);
        }

        curr++;
        entries++;
    }
}

void LayerLoader::LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer* curr,
                                     char const* const* entries) {
    while (*entries) {
        char const* name = *entries;
        EGLFuncPointer prev = *curr;

        // Only apply layers to driver entries if not handled by the platform
        if (FindPlatformImplAddr(name) == nullptr) {
            // Pass the existing entry point into the layer, replace the call with return value
            *curr = ApplyLayer(layer_setup, name, *prev);

            if (prev != *curr) {
                ALOGV("LayerDriverEntries: Replaced (%llu) with platform entry (%llu), for %s",
                      (unsigned long long)prev, (unsigned long long)*curr, name);
            }

        } else {
            ALOGV("LayerDriverEntries: Skipped (%llu) for %s", (unsigned long long)prev, name);
        }

        curr++;
        entries++;
    }
}

bool LayerLoader::Initialized() {
    return initialized_;
}

void LayerLoader::InitLayers(egl_connection_t* cnx) {
    if (!layers_loaded_) return;

    if (initialized_) return;

    if (layer_setup_.empty()) {
        initialized_ = true;
        return;
    }

    // Include the driver in layer_functions
    layer_functions.resize(layer_setup_.size() + 1);

    // Walk through the initial lists and create layer_functions[0]
    int func_idx = 0;
    char const* const* entries;
    EGLFuncPointer* curr;

    entries = platform_names;
    curr = reinterpret_cast<EGLFuncPointer*>(&cnx->platform);
    SetupFuncMaps(layer_functions[0], entries, curr, func_idx);
    ALOGV("InitLayers: func_idx after platform_names: %i", func_idx);

    entries = egl_names;
    curr = reinterpret_cast<EGLFuncPointer*>(&cnx->egl);
    SetupFuncMaps(layer_functions[0], entries, curr, func_idx);
    ALOGV("InitLayers: func_idx after egl_names: %i", func_idx);

    entries = gl_names;
    curr = reinterpret_cast<EGLFuncPointer*>(&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl);
    SetupFuncMaps(layer_functions[0], entries, curr, func_idx);
    ALOGV("InitLayers: func_idx after gl_names: %i", func_idx);

    // Walk through each layer's entry points per API, starting just above the driver
    for (current_layer_ = 0; current_layer_ < layer_setup_.size(); current_layer_++) {
        // Init the layer with a key that points to layer just below it
        layer_init_[current_layer_](reinterpret_cast<void*>(&layer_functions[current_layer_]),
                                    reinterpret_cast<PFNEGLGETNEXTLAYERPROCADDRESSPROC>(
                                            getNextLayerProcAddress));

        // Check functions implemented by the platform
        func_idx = 0;
        entries = platform_names;
        curr = reinterpret_cast<EGLFuncPointer*>(&cnx->platform);
        LayerPlatformEntries(layer_setup_[current_layer_], curr, entries);

        // Populate next function table after layers have been applied
        SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx);

        // EGL
        entries = egl_names;
        curr = reinterpret_cast<EGLFuncPointer*>(&cnx->egl);
        LayerDriverEntries(layer_setup_[current_layer_], curr, entries);

        // Populate next function table after layers have been applied
        SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx);

        // GLES 2+
        // NOTE: We route calls to GLESv2 hooks, not GLESv1, so layering does not support GLES 1.x
        // If it were added in the future, a different layer initialization model would be needed,
        // that defers loading GLES entrypoints until after eglMakeCurrent, so two phase
        // initialization.
        entries = gl_names;
        curr = reinterpret_cast<EGLFuncPointer*>(&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl);
        LayerDriverEntries(layer_setup_[current_layer_], curr, entries);

        // Populate next function table after layers have been applied
        SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx);
    }

    // We only want to apply layers once
    initialized_ = true;
}

void LayerLoader::LoadLayers() {
    std::string debug_layers = GetDebugLayers();

    // If no layers are specified, we're done
    if (debug_layers.empty()) return;

    // Only enable the system search path for non-user builds
    std::string system_path;
    if (android::GraphicsEnv::getInstance().isDebuggable()) {
        system_path = kSystemLayerLibraryDir;
    }

    ALOGI("Debug layer list: %s", debug_layers.c_str());
    std::vector<std::string> layers = android::base::Split(debug_layers, ":");

    // Load the layers in reverse order so we start with the driver's entrypoint and work our way up
    for (int32_t i = layers.size() - 1; i >= 0; i--) {
        // Check each layer path for the layer
        std::vector<std::string> paths =
                android::base::Split(android::GraphicsEnv::getInstance().getLayerPaths().c_str(),
                                     ":");

        if (!system_path.empty()) {
            // Prepend the system paths so they override other layers
            auto it = paths.begin();
            paths.insert(it, system_path);
        }

        bool layer_found = false;
        for (uint32_t j = 0; j < paths.size() && !layer_found; j++) {
            std::string layer;

            ALOGI("Searching %s for GLES layers", paths[j].c_str());

            // Realpath will return null for non-existent files
            android::base::Realpath(paths[j] + "/" + layers[i], &layer);

            if (!layer.empty()) {
                layer_found = true;
                ALOGI("GLES layer found: %s", layer.c_str());

                // Load the layer
                //
                // TODO: This code is common with Vulkan loader, refactor
                //
                // Libraries in the system layer library dir can't be loaded into
                // the application namespace. That causes compatibility problems, since
                // any symbol dependencies will be resolved by system libraries. They
                // can't safely use libc++_shared, for example. Which is one reason
                // (among several) we only allow them in non-user builds.
                auto app_namespace = android::GraphicsEnv::getInstance().getAppNamespace();
                if (app_namespace && !android::base::StartsWith(layer, kSystemLayerLibraryDir)) {
                    char* error_message = nullptr;
                    dlhandle_ = OpenNativeLibraryInNamespace(
                        app_namespace, layer.c_str(), &native_bridge_, &error_message);
                    if (!dlhandle_) {
                        ALOGE("Failed to load layer %s with error: %s", layer.c_str(),
                              error_message);
                        android::NativeLoaderFreeErrorMessage(error_message);
                        return;
                    }

                } else {
                    dlhandle_ = dlopen(layer.c_str(), RTLD_NOW | RTLD_LOCAL);
                }

                if (dlhandle_) {
                    ALOGV("Loaded layer handle (%llu) for layer %s", (unsigned long long)dlhandle_,
                          layers[i].c_str());
                } else {
                    // If the layer is found but can't be loaded, try setenforce 0
                    const char* dlsym_error = dlerror();
                    ALOGE("Failed to load layer %s with error: %s", layer.c_str(), dlsym_error);
                    return;
                }

                // Find the layer's Initialize function
                std::string init_func = "AndroidGLESLayer_Initialize";
                ALOGV("Looking for entrypoint %s", init_func.c_str());

                layer_init_func LayerInit = GetTrampoline<layer_init_func>(init_func.c_str());
                if (LayerInit) {
                    ALOGV("Found %s for layer %s", init_func.c_str(), layer.c_str());
                    layer_init_.push_back(LayerInit);
                } else {
                    ALOGE("Failed to dlsym %s for layer %s", init_func.c_str(), layer.c_str());
                    return;
                }

                // Find the layer's setup function
                std::string setup_func = "AndroidGLESLayer_GetProcAddress";
                ALOGV("Looking for entrypoint %s", setup_func.c_str());

                layer_setup_func LayerSetup = GetTrampoline<layer_setup_func>(setup_func.c_str());
                if (LayerSetup) {
                    ALOGV("Found %s for layer %s", setup_func.c_str(), layer.c_str());
                    layer_setup_.push_back(LayerSetup);
                } else {
                    ALOGE("Failed to dlsym %s for layer %s", setup_func.c_str(), layer.c_str());
                    return;
                }
            }
        }
    }
    // Track this so we only attempt to load these once
    layers_loaded_ = true;
}

} // namespace android