LibreOffice Module bridges (master)  1
msvc_win32_x86-64/except.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 // Interesting info can be found in:
21 
22 // MSDN, obviously
23 
24 // http://www.osronline.com/article.cfm?article=469
25 
26 // ONTL, "Open NT Native Template Library", a C++ STL-like library
27 // that can be used even when writing Windows drivers. This is some
28 // quite badass code. The author has done impressive heavy spelunking
29 // of MSVCR structures. http://code.google.com/p/ontl/
30 
31 // Geoff Chappell's pages:
32 // http://www.geoffchappell.com/studies/msvc/language/index.htm
33 
34 // The below is from ONTL's ntl/nt/exception.hxx, cleaned up to keep just the _M_X64 parts:
35 
36 #if 0
37 
38 /* This information until the corresponding '#endif // 0' is covered
39  * by ONTL's license, which is said to be the "zlib/libpng license"
40  * below, which as far as I see is permissive enough to allow this
41  * information to be included here in this source file. Note that no
42  * actual code from ONTL below gets compiled into the object file.
43  */
44 
45 /*
46  * Copyright (c) 2011 <copyright holders> (The ONTL main
47  * developer(s) don't tell their real name(s) on the ONTL site.)
48  *
49  * This software is provided 'as-is', without any express or implied
50  * warranty. In no event will the authors be held liable for any damages
51  * arising from the use of this software.
52  *
53  * Permission is granted to anyone to use this software for any purpose,
54  * including commercial applications, and to alter it and redistribute it
55  * freely, subject to the following restrictions:
56  *
57  * 1. The origin of this software must not be misrepresented; you must not
58  * claim that you wrote the original software. If you use this software
59  * in a product, an acknowledgment in the product documentation would be
60  * appreciated but is not required.
61  *
62  * 2. Altered source versions must be plainly marked as such, and must not be
63  * misrepresented as being the original software.
64  *
65  * 3. This notice may not be removed or altered from any source
66  * distribution.
67  *
68  */
69 
70 typedef uint32_t rva_t;
71 
73 typedef void generic_function_t();
74 
75 struct ptrtomember // _PMD
76 {
77  typedef __w64 int32_t mdiff_t;
78  mdiff_t member_offset;
79  mdiff_t vbtable_offset; // -1 if not a virtual base
80  mdiff_t vdisp_offset; // offset to the displacement value inside the vbtable
81 
82  template<typename T>
83  T * operator()(T * const thisptr) const
84  {
85  uintptr_t tp = reinterpret_cast<uintptr_t>(thisptr);
86  uintptr_t ptr = tp + member_offset;
87  if ( vbtable_offset != -1 ) // !(vbtable_offset < 0)
88  {
89  ptr += *reinterpret_cast<mdiff_t*>( static_cast<intptr_t>(vdisp_offset + *reinterpret_cast<mdiff_t*>(tp + vbtable_offset)) )
90  + vbtable_offset;
91  }
92  return reinterpret_cast<T*>(ptr);
93  }
94 };
95 
96 struct eobject
97 {
98  typedef void (* dtor_ptr )(eobject*);
99  typedef void (* ctor_ptr )(eobject*, eobject*);
100  typedef void (* ctor_ptr2)(eobject*, eobject*, int);
101 };
102 
103 struct catchabletype
104 {
106  uint32_t memmoveable : 1;
108  uint32_t refonly : 1;
110  uint32_t hasvirtbase : 1;
112  rva_t typeinfo;
113 
115  ptrtomember thiscast;
117  uint32_t object_size;
118 
119  union
120  {
121  rva_t copyctor;
122  rva_t copyctor2;
123  };
124 };
125 
126 #pragma pack(push, 4)
127 struct catchabletypearray
128 {
129  uint32_t size;
130  rva_t type[1];
131 };
132 #pragma pack(pop)
133 
134 #pragma pack(push, 4)
135 struct throwinfo
136 {
137  typedef exception_disposition __cdecl forwardcompathandler_t(...);
138 
139  /* 0x00 */ uint32_t econst : 1;
140  /* 0x00 */ uint32_t evolatile : 1;
141  /* 0x00 */ uint32_t : 1;
142  /* 0x00 */ uint32_t e8 : 1;
143  /* 0x04 */ rva_t exception_dtor;
144  /* 0x08 */ rva_t forwardcompathandler;
145  /* 0x0C */ rva_t catchabletypearray;
146 };
147 #pragma pack(pop)
148 
150 struct ehandler
151 {
152  // union { uint32_t adjectives; void * ptr; };
153  uint32_t isconst : 1;
154  uint32_t isvolatile : 1;
155  uint32_t isunaligned : 1;// guess it is not used on x86
156  uint32_t isreference : 1;
157 
158  uint32_t :27;
159  uint32_t ishz : 1;
160 
162  /*0x04*/ rva_t typeinfo; // dispType
163  /*0x08*/ int eobject_bpoffset; // dispCatchObj
165  /*0x0C*/ rva_t handler; // dispOfHandler
166  /*0x10*/ uint32_t frame; // dispFrame
167 }
168 
169 // ___BuildCatchObject
177 void
178  constructcatchobject(
179  cxxregistration * cxxreg,
180  const ehandler * const catchblock,
181  catchabletype * const convertible,
182  const dispatcher_context* const dispatch
183  )
184  const
185 {
186  _EH_TRACE_ENTER();
187  // build helper
188  __try {
189  struct typeinfo_t { void* vtbl; void* spare; char name[1]; };
190  enum catchable_info { cidefault, cicomplex, civirtual } cinfo = cidefault;
191 
192  const typeinfo_t* ti = catchblock->typeinfo ? dispatch->va<typeinfo_t*>(catchblock->typeinfo) : NULL;
193  if(ti && *ti->name && (catchblock->eobject_bpoffset || catchblock->ishz)){
194  eobject** objplace = catchblock->ishz
195  ? reinterpret_cast<eobject**>(cxxreg)
196  : reinterpret_cast<eobject**>(catchblock->eobject_bpoffset + cxxreg->fp.FramePointers);
197  if(catchblock->isreference){
198  // just ref/pointer
199  *objplace = adjust_pointer(get_object(), convertible);
200  }else if(convertible->memmoveable){
201  // POD
202  std::memcpy(objplace, get_object(), convertible->object_size);
203  if(convertible->object_size == sizeof(void*) && *objplace)
204  *objplace = adjust_pointer((void*)*objplace, convertible);
205  }else{
206  // if copy ctor exists, call it; binary copy otherwise
207  if(convertible->copyctor){
208  cinfo = convertible->hasvirtbase ? civirtual : cicomplex;
209  }else{
210  std::memcpy(objplace, (const void*)adjust_pointer(get_object(), convertible), convertible->object_size);
211  }
212  }
213  }
214  // end of build helper
215  if(cinfo != cidefault){
216  eobject* objthis = catchblock->ishz
217  ? reinterpret_cast<eobject*>(cxxreg)
218  : reinterpret_cast<eobject*>(catchblock->eobject_bpoffset + cxxreg->fp.FramePointers);
219  void* copyctor = thrown_va(convertible->copyctor);
220  eobject* copyarg = adjust_pointer(get_object(), convertible);
221  if(cinfo == cicomplex)
222  (eobject::ctor_ptr (copyctor))(objthis, copyarg);
223  else
224  (eobject::ctor_ptr2(copyctor))(objthis, copyarg, 1);
225  }
226  }
227  __except(cxxregistration::unwindfilter(static_cast<nt::ntstatus>(_exception_code())))
228  {
229  nt::exception::inconsistency();
230  }
231  _EH_TRACE_LEAVE();
232 }
233 
234 #endif // 0
235 
236 #include <sal/config.h>
237 
238 #include <memory>
239 
240 #include <malloc.h>
241 #include <new.h>
242 #include <typeinfo>
243 #include <signal.h>
244 
245 #include <rtl/alloc.h>
246 #include <rtl/strbuf.hxx>
247 #include <rtl/ustrbuf.hxx>
248 #include <sal/log.hxx>
249 
250 #include <com/sun/star/uno/Any.hxx>
251 #include <msvc/amd64.hxx>
252 #include <except.hxx>
253 
254 #pragma pack(push, 8)
255 
256 using namespace ::com::sun::star;
257 
258 static void* __cdecl copyConstruct(void* pExcThis, void* pSource,
259  typelib_TypeDescription* pTD) noexcept
260 {
261  ::uno_copyData(pExcThis, pSource, pTD, uno::cpp_acquire);
262  return pExcThis;
263 }
264 
265 static void* __cdecl destruct(void* pExcThis, typelib_TypeDescription* pTD) noexcept
266 {
267  ::uno_destructData(pExcThis, pTD, uno::cpp_release);
268  return pExcThis;
269 }
270 
271 const int codeSnippetSize = 40;
272 
273 static void GenerateConstructorTrampoline(unsigned char* code,
274  typelib_TypeDescription* pTD) noexcept
275 {
276  unsigned char* p = code;
277 
278  // mov r8, pTD
279  *p++ = 0x49;
280  *p++ = 0xB8;
281  *reinterpret_cast<void**>(p) = pTD;
282  p += 8;
283 
284  // mov r11, copyConstruct
285  *p++ = 0x49;
286  *p++ = 0xBB;
287  *reinterpret_cast<void**>(p) = reinterpret_cast<void*>(&copyConstruct);
288  p += 8;
289 
290  // jmp r11
291  *p++ = 0x41;
292  *p++ = 0xFF;
293  *p++ = 0xE3;
294 
295  assert(p < code + codeSnippetSize);
296 }
297 
298 static void GenerateDestructorTrampoline(unsigned char* code, typelib_TypeDescription* pTD) noexcept
299 {
300  unsigned char* p = code;
301 
302  // mov rdx, pTD
303  *p++ = 0x48;
304  *p++ = 0xBA;
305  *reinterpret_cast<void**>(p) = pTD;
306  p += 8;
307 
308  // mov r11, destruct
309  *p++ = 0x49;
310  *p++ = 0xBB;
311  *reinterpret_cast<void**>(p) = reinterpret_cast<void*>(&destruct);
312  p += 8;
313 
314  // jmp r11
315  *p++ = 0x41;
316  *p++ = 0xFF;
317  *p++ = 0xE3;
318 
319  assert(p < code + codeSnippetSize);
320 }
321 
322 ExceptionType::ExceptionType(unsigned char* pCode, sal_uInt64 pCodeBase,
323  typelib_TypeDescription* pTD) noexcept
324  : _n0(0)
325  , _n1(0)
326  , _n2(-1)
327  , _n3(0)
328  , _n4(pTD->nSize)
329  , exc_type_info(nullptr, "")
330 {
331  // As _n0 is always initialized to zero, that means the
332  // hasvirtbase flag (see the ONTL catchabletype struct) is
333  // off, and thus the copyctor is of the ctor_ptr kind.
334 
335  int len;
336  type_info* pRTTI = RTTInfos::get(pTD->pTypeName, &len);
337 
338  memcpy(static_cast<void*>(&exc_type_info), static_cast<void*>(pRTTI), len);
339  _pTypeInfo = static_cast<sal_uInt32>(reinterpret_cast<sal_uInt64>(&exc_type_info) - pCodeBase);
340  GenerateConstructorTrampoline(pCode, pTD);
341 
342  assert(pCodeBase <= reinterpret_cast<sal_uInt64>(pCode)
343  && (reinterpret_cast<sal_uInt64>(pCode) - pCodeBase < 0x100000000));
344  _pCopyCtor = static_cast<sal_uInt32>(reinterpret_cast<sal_uInt64>(pCode) - pCodeBase);
345 }
346 
347 /* Rewrite of 32-Bit-Code to work under 64 Bit:
348 * To use the 32 Bit offset values in the ExceptionType we have to
349 * allocate a single allocation block and use it for all code and date
350 * all offsets inside this area are guaranteed to be in 32 bit address range.
351 * So we have to calc total memory allocation size for D-tor, C-Tors,
352 * ExceptionType and type_info. ExceptionType is allocated via placement new
353 * to locate everything inside our mem block.
354 * There is one caveat: Struct type_info is kept in
355 * a map and was referenced from class ExceptionType. Therefore type_info now
356 * is also member of ExceptionType and can be referenced via 32 bit offset.
357 */
358 
360  : _n0(0)
361  , _n2(0)
362  , _pTD(pTD)
363 {
364  typelib_CompoundTypeDescription* pCompTD;
365 
366  // Count how many trampolines we need
367  int codeSize = codeSnippetSize;
368 
369  // Info count
370  int nLen = 0;
371  for (pCompTD = reinterpret_cast<typelib_CompoundTypeDescription*>(pTD); pCompTD;
372  pCompTD = pCompTD->pBaseTypeDescription)
373  {
374  ++nLen;
375  codeSize += codeSnippetSize;
376  }
377 
378  // Array with size (4) and all _pTypeInfo (4*nLen)
379  int typeInfoArraySize = 4 + 4 * nLen;
380 
381  // 2.Pass: Get the total needed memory for class ExceptionType
382  // (with embedded type_info) and keep the sizes for each instance
383  // is stored in allocated int array
384  auto exceptionTypeSizeArray = std::make_unique<int[]>(nLen);
385 
386  nLen = 0;
387  for (pCompTD = reinterpret_cast<typelib_CompoundTypeDescription*>(pTD); pCompTD;
388  pCompTD = pCompTD->pBaseTypeDescription)
389  {
390  int typeInfoLen;
391  RTTInfos::get(pCompTD->aBase.pTypeName, &typeInfoLen);
392  // Mem has to be on 4-byte Boundary
393  if (typeInfoLen % 4 != 0)
394  {
395  int n = typeInfoLen / 4;
396  n++;
397  typeInfoLen = n * 4;
398  }
399  exceptionTypeSizeArray[nLen++] = typeInfoLen + sizeof(ExceptionType);
400  }
401 
402  // Total ExceptionType related mem
403  int excTypeAddLen = 0;
404  for (int i = 0; i < nLen; i++)
405  {
406  excTypeAddLen += exceptionTypeSizeArray[i];
407  }
408 
409  // Allocate mem for code and all dynamic data in one chunk to guarantee
410  // 32 bit offsets
411  const int totalSize = codeSize + typeInfoArraySize + excTypeAddLen;
412  unsigned char* pCode = _code = static_cast<unsigned char*>(std::malloc(totalSize));
413  int pCodeOffset = 0;
414 
415  // New base of types array, starts after Trampoline D-Tor / C-Tors
416  DWORD* types = reinterpret_cast<DWORD*>(pCode + codeSize);
417 
418  // New base of ExceptionType array, starts after types array
419  unsigned char* etMem = pCode + codeSize + typeInfoArraySize;
420  int etMemOffset = 0;
421 
422  _codeBase = reinterpret_cast<sal_uInt64>(pCode)
423  & ~static_cast<sal_uInt64>(ExceptionInfos::allocationGranularity - 1);
424 
425  DWORD old_protect;
426  bool success = VirtualProtect(pCode, codeSize, PAGE_EXECUTE_READWRITE, &old_protect);
427  (void)success;
428  assert(success && "VirtualProtect() failed!");
429 
430  ::typelib_typedescription_acquire(pTD);
431 
432  // Fill pCode with D-Tor code
433  GenerateDestructorTrampoline(pCode, pTD);
434  _pDtor = static_cast<sal_Int32>(reinterpret_cast<sal_uInt64>(pCode) - _codeBase);
435  pCodeOffset += codeSnippetSize;
436 
437  // Info count accompanied by type info ptrs: type, base type, base base type, ...
438  // Keep offset of types_array
439  _types = static_cast<sal_Int32>(reinterpret_cast<sal_uInt64>(types) - _codeBase);
440  // Fill types: (nLen, _offset to ExceptionType1, ...ExceptionType2, ...)
441  types[0] = nLen;
442 
443  int nPos = 1;
444  for (pCompTD = reinterpret_cast<typelib_CompoundTypeDescription*>(pTD); pCompTD;
445  pCompTD = pCompTD->pBaseTypeDescription)
446  {
447  // Create instance in mem block with placement new
448  ExceptionType* et = new (etMem + etMemOffset) ExceptionType(
449  pCode + pCodeOffset, _codeBase, reinterpret_cast<typelib_TypeDescription*>(pCompTD));
450 
451  // Next trampoline entry offset
452  pCodeOffset += codeSnippetSize;
453  // Next ExceptionType placement offset
454  etMemOffset += exceptionTypeSizeArray[nPos - 1];
455 
456  // Keep offset of addresses of ET for D-Tor call in ~RaiseInfo
457  types[nPos++] = static_cast<DWORD>(reinterpret_cast<sal_uInt64>(et) - _codeBase);
458  }
459  // Final check: end of address calculation must be end of mem
460  assert(etMem + etMemOffset == pCode + totalSize);
461 }
462 
463 #pragma pack(pop)
464 
465 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static void GenerateConstructorTrampoline(unsigned char *code, typelib_TypeDescription *pTD) noexcept
static DWORD allocationGranularity
ExceptionType(unsigned char *pCode, sal_uInt64 pCodeBase, typelib_TypeDescription *pTD) noexcept
exports com.sun.star.lib.uno. typeinfo
RaiseInfo(typelib_TypeDescription *pTD) noexcept
const wchar_t *typedef int(__stdcall *DllNativeUnregProc)(int
sal_Int64 n
sal_Unicode code
exports com.sun.star. frame
static void *__cdecl destruct(void *pExcThis, typelib_TypeDescription *pTD) noexcept
int i
static void GenerateDestructorTrampoline(unsigned char *code, typelib_TypeDescription *pTD) noexcept
static void *__cdecl copyConstruct(void *pExcThis, void *pSource, typelib_TypeDescription *pTD) noexcept
size
struct _typelib_TypeDescription typelib_TypeDescription
Definition: msvc/except.hxx:52
static type_info * get(OUString const &rUNOname, int *len=nullptr) noexcept
void * p
const int codeSnippetSize
ResultType type
sal_uInt16 nPos
char const * name
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo