tdf#130857 qt weld: Support mail merge "Server Auth" dialog
[LibreOffice.git] / bridges / source / cpp_uno / shared / vtablefactory.cxx
blob10db7cc505a7970ad58ad527f206ab9b6d4209bd
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <vtablefactory.hxx>
23 #include <vtables.hxx>
25 #include <osl/thread.h>
26 #include <osl/security.hxx>
27 #include <osl/file.hxx>
28 #include <rtl/alloc.h>
29 #include <rtl/ustring.hxx>
30 #include <sal/log.hxx>
31 #include <sal/types.h>
33 #include <memory>
34 #include <new>
35 #include <unordered_map>
36 #include <vector>
38 #if defined SAL_UNX
39 #include <unistd.h>
40 #include <string.h>
41 #include <errno.h>
42 #include <sys/mman.h>
43 #elif defined _WIN32
44 #define WIN32_LEAN_AND_MEAN
45 #include <windows.h>
46 #else
47 #error Unsupported platform
48 #endif
50 #if defined USE_DOUBLE_MMAP
51 #include <fcntl.h>
52 #endif
54 #if defined MACOSX && defined __aarch64__
55 #include <pthread.h>
56 #endif
58 using bridges::cpp_uno::shared::VtableFactory;
60 namespace {
62 extern "C" void * allocExec(
63 SAL_UNUSED_PARAMETER rtl_arena_type *, sal_Size * size)
65 std::size_t pagesize;
66 #if defined SAL_UNX
67 #if defined FREEBSD || defined NETBSD || defined OPENBSD || defined DRAGONFLY || defined HAIKU
68 pagesize = getpagesize();
69 #else
70 // coverity[ tainted_data_return : FALSE ] version 2023.12.2
71 pagesize = sysconf(_SC_PAGESIZE);
72 #endif
73 #elif defined _WIN32
74 SYSTEM_INFO info;
75 GetSystemInfo(&info);
76 pagesize = info.dwPageSize;
77 #else
78 #error Unsupported platform
79 #endif
80 std::size_t n = (*size + (pagesize - 1)) & ~(pagesize - 1);
81 void * p;
82 #if defined SAL_UNX
83 #if defined MACOSX
84 p = mmap(
85 nullptr, n, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON | MAP_JIT, -1,
86 0);
87 if (p != MAP_FAILED) {
88 goto done;
91 auto const e = errno;
92 SAL_INFO("bridges.osx", "mmap failed with " << e);
93 if (e != EINVAL) {
94 p = nullptr;
95 goto done;
98 // At least some macOS 10.13 machines are reported to fail the above mmap with EINVAL (see
99 // tdf#134754 "Crash on macOS 10.13 opening local HSQLDB-based odb file in Base on LibreOffice 7
100 // rc1", so in that case retry with the "traditional" approach:
101 #endif
102 p = mmap(
103 nullptr, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1,
105 if (p == MAP_FAILED) {
106 p = nullptr;
108 else if (mprotect (p, n, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
110 munmap (p, n);
111 p = nullptr;
113 #if defined MACOSX
114 done:
115 #endif
116 #elif defined _WIN32
117 p = VirtualAlloc(nullptr, n, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
118 #endif
119 if (p != nullptr) {
120 *size = n;
122 return p;
125 extern "C" void freeExec(
126 SAL_UNUSED_PARAMETER rtl_arena_type *, void * address, sal_Size size)
128 #if defined SAL_UNX
129 munmap(address, size);
130 #elif defined _WIN32
131 (void) size; // unused
132 VirtualFree(address, 0, MEM_RELEASE);
133 #endif
136 #if defined MACOSX && defined __aarch64__
137 struct JitMemoryProtectionGuard {
138 JitMemoryProtectionGuard() { pthread_jit_write_protect_np(0); }
139 ~JitMemoryProtectionGuard() { pthread_jit_write_protect_np(1); }
141 #endif
145 class VtableFactory::GuardedBlocks:
146 public std::vector<Block>
148 public:
149 GuardedBlocks(const GuardedBlocks&) = delete;
150 const GuardedBlocks& operator=(const GuardedBlocks&) = delete;
152 explicit GuardedBlocks(VtableFactory const & factory):
153 m_factory(factory), m_guarded(true) {}
155 ~GuardedBlocks();
157 void unguard() { m_guarded = false; }
159 private:
160 VtableFactory const & m_factory;
161 bool m_guarded;
164 VtableFactory::GuardedBlocks::~GuardedBlocks() {
165 if (m_guarded) {
166 for (iterator i(begin()); i != end(); ++i) {
167 m_factory.freeBlock(*i);
172 class VtableFactory::BaseOffset {
173 public:
174 explicit BaseOffset(typelib_InterfaceTypeDescription * type) { calculate(type, 0); }
176 sal_Int32 getFunctionOffset(OUString const & name) const
177 { return m_map.find(name)->second; }
179 private:
180 sal_Int32 calculate(
181 typelib_InterfaceTypeDescription * type, sal_Int32 offset);
183 std::unordered_map< OUString, sal_Int32 > m_map;
186 sal_Int32 VtableFactory::BaseOffset::calculate(
187 typelib_InterfaceTypeDescription * type, sal_Int32 offset)
189 OUString name(type->aBase.pTypeName);
190 if (m_map.find(name) == m_map.end()) {
191 for (sal_Int32 i = 0; i < type->nBaseTypes; ++i) {
192 offset = calculate(type->ppBaseTypes[i], offset);
194 m_map.insert({name, offset});
195 typelib_typedescription_complete(
196 reinterpret_cast< typelib_TypeDescription ** >(&type));
197 offset += bridges::cpp_uno::shared::getLocalFunctions(type);
199 return offset;
202 VtableFactory::VtableFactory(): m_arena(
203 rtl_arena_create(
204 "bridges::cpp_uno::shared::VtableFactory",
205 sizeof (void *), // to satisfy alignment requirements
206 0, nullptr, allocExec, freeExec, 0))
208 if (m_arena == nullptr) {
209 throw std::bad_alloc();
213 VtableFactory::~VtableFactory() {
215 std::scoped_lock guard(m_mutex);
216 for (const auto& rEntry : m_map) {
217 for (sal_Int32 j = 0; j < rEntry.second.count; ++j) {
218 freeBlock(rEntry.second.blocks[j]);
222 rtl_arena_destroy(m_arena);
225 const VtableFactory::Vtables& VtableFactory::getVtables(
226 typelib_InterfaceTypeDescription * type)
228 OUString name(type->aBase.pTypeName);
229 std::scoped_lock guard(m_mutex);
230 Map::iterator i(m_map.find(name));
231 if (i == m_map.end()) {
232 GuardedBlocks blocks(*this);
233 createVtables(blocks, BaseOffset(type), type, 0, type, true);
234 Vtables vtables;
235 assert(blocks.size() <= SAL_MAX_INT32);
236 vtables.count = static_cast< sal_Int32 >(blocks.size());
237 vtables.blocks.reset(new Block[vtables.count]);
238 for (sal_Int32 j = 0; j < vtables.count; ++j) {
239 vtables.blocks[j] = blocks[j];
241 i = m_map.emplace(name, std::move(vtables)).first;
242 blocks.unguard();
244 return i->second;
247 #ifdef USE_DOUBLE_MMAP
248 bool VtableFactory::createBlock(Block &block, sal_Int32 slotCount) const
250 std::size_t size = getBlockSize(slotCount);
251 std::size_t pagesize = sysconf(_SC_PAGESIZE);
252 block.size = (size + (pagesize - 1)) & ~(pagesize - 1);
253 block.fd = -1;
255 // Try non-doublemmaped allocation first:
256 block.start = block.exec = rtl_arena_alloc(m_arena, &block.size);
257 if (block.start != nullptr) {
258 return true;
261 osl::Security aSecurity;
262 OUString strDirectory;
263 OUString strURLDirectory;
264 if (aSecurity.getHomeDir(strURLDirectory))
265 osl::File::getSystemPathFromFileURL(strURLDirectory, strDirectory);
267 for (int i = strDirectory.isEmpty() ? 1 : 0; i < 2; ++i)
269 if (strDirectory.isEmpty())
270 strDirectory = "/tmp";
272 strDirectory += "/.execoooXXXXXX";
273 OString aTmpName = OUStringToOString(strDirectory, osl_getThreadTextEncoding());
274 std::unique_ptr<char[]> tmpfname(new char[aTmpName.getLength()+1]);
275 strncpy(tmpfname.get(), aTmpName.getStr(), aTmpName.getLength()+1);
276 // coverity[secure_temp] - https://communities.coverity.com/thread/3179
277 if ((block.fd = mkstemp(tmpfname.get())) == -1)
278 fprintf(stderr, "mkstemp(\"%s\") failed: %s\n", tmpfname.get(), strerror(errno));
279 if (block.fd == -1)
281 break;
283 unlink(tmpfname.get());
284 tmpfname.reset();
285 #if defined(HAVE_POSIX_FALLOCATE)
286 int err = posix_fallocate(block.fd, 0, block.size);
287 #else
288 int err = ftruncate(block.fd, block.size);
289 #endif
290 if (err != 0)
292 #if defined(HAVE_POSIX_FALLOCATE)
293 SAL_WARN("bridges", "posix_fallocate failed with code " << err);
294 #else
295 SAL_WARN("bridges", "truncation of executable memory area failed with code " << err);
296 #endif
297 close(block.fd);
298 block.fd = -1;
299 break;
301 block.start = mmap(nullptr, block.size, PROT_READ | PROT_WRITE, MAP_SHARED, block.fd, 0);
302 if (block.start== MAP_FAILED) {
303 block.start = nullptr;
305 block.exec = mmap(nullptr, block.size, PROT_READ | PROT_EXEC, MAP_SHARED, block.fd, 0);
306 if (block.exec == MAP_FAILED) {
307 block.exec = nullptr;
310 //All good
311 if (block.start && block.exec && block.fd != -1)
312 break;
314 freeBlock(block);
316 strDirectory.clear();
318 return (block.start != nullptr && block.exec != nullptr);
321 void VtableFactory::freeBlock(Block const & block) const {
322 //if the double-map failed we were allocated on the arena
323 if (block.fd == -1 && block.start == block.exec && block.start != nullptr)
324 rtl_arena_free(m_arena, block.start, block.size);
325 else
327 if (block.start) munmap(block.start, block.size);
328 if (block.exec) munmap(block.exec, block.size);
329 if (block.fd != -1) close(block.fd);
332 #else
333 bool VtableFactory::createBlock(Block &block, sal_Int32 slotCount) const
335 block.size = getBlockSize(slotCount);
336 block.start = rtl_arena_alloc(m_arena, &block.size);
337 return block.start != nullptr;
340 void VtableFactory::freeBlock(Block const & block) const {
341 rtl_arena_free(m_arena, block.start, block.size);
343 #endif
345 sal_Int32 VtableFactory::createVtables(
346 GuardedBlocks & blocks, BaseOffset const & baseOffset,
347 typelib_InterfaceTypeDescription * type, sal_Int32 vtableNumber,
348 typelib_InterfaceTypeDescription * mostDerived, bool includePrimary) const
351 #if defined MACOSX && defined __aarch64__
352 JitMemoryProtectionGuard guard;
353 #endif
354 if (includePrimary) {
355 sal_Int32 slotCount
356 = bridges::cpp_uno::shared::getPrimaryFunctions(type);
357 Block block;
358 if (!createBlock(block, slotCount)) {
359 throw std::bad_alloc();
361 try {
362 Slot * slots = initializeBlock(
363 block.start, slotCount, vtableNumber, mostDerived);
364 unsigned char * codeBegin =
365 reinterpret_cast< unsigned char * >(slots);
366 unsigned char * code = codeBegin;
367 sal_Int32 vtableOffset = blocks.size() * sizeof (Slot *);
368 for (typelib_InterfaceTypeDescription const * type2 = type;
369 type2 != nullptr; type2 = type2->pBaseTypeDescription)
371 code = addLocalFunctions(
372 &slots, code,
373 #ifdef USE_DOUBLE_MMAP
374 reinterpret_cast<sal_uIntPtr>(block.exec) - reinterpret_cast<sal_uIntPtr>(block.start),
375 #endif
376 type2,
377 baseOffset.getFunctionOffset(type2->aBase.pTypeName),
378 bridges::cpp_uno::shared::getLocalFunctions(type2),
379 vtableOffset);
381 flushCode(codeBegin, code);
382 #ifdef USE_DOUBLE_MMAP
383 //Finished generating block, swap writable pointer with executable
384 //pointer
385 std::swap(block.start, block.exec);
386 #endif
387 blocks.push_back(block);
388 } catch (...) {
389 freeBlock(block);
390 throw;
394 for (sal_Int32 i = 0; i < type->nBaseTypes; ++i) {
395 vtableNumber = createVtables(
396 blocks, baseOffset, type->ppBaseTypes[i],
397 vtableNumber + (i == 0 ? 0 : 1), mostDerived, i != 0);
399 return vtableNumber;
402 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */