go-trampoline.c 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. /* go-trampoline.c -- allocate a trampoline for a nested function.
  2. Copyright 2009 The Go Authors. All rights reserved.
  3. Use of this source code is governed by a BSD-style
  4. license that can be found in the LICENSE file. */
  5. #include "config.h"
  6. #include <stddef.h>
  7. #include <stdint.h>
  8. #include <unistd.h>
  9. #ifdef HAVE_SYS_MMAN_H
  10. #include <sys/mman.h>
  11. #endif
  12. #include "runtime.h"
  13. #include "arch.h"
  14. #include "malloc.h"
  15. #include "go-assert.h"
  16. /* Trampolines need to run in memory that is both writable and
  17. executable. In order to implement them, we grab a page of memory
  18. and mprotect it. We fill in the page with trampolines as they are
  19. required. When we run out of space, we drop the pointer to the
  20. page and allocate a new one. The page will be freed by the garbage
  21. collector when there are no more variables of type func pointing to
  22. it. */
  23. /* A lock to control access to the page of closures. */
  24. static Lock trampoline_lock;
  25. /* The page of closures. */
  26. static unsigned char *trampoline_page;
  27. /* The size of trampoline_page. */
  28. static uintptr_t trampoline_page_size;
  29. /* The number of bytes we have used on trampoline_page. */
  30. static uintptr_t trampoline_page_used;
  31. /* Allocate a trampoline of SIZE bytes that will use the closure in
  32. CLOSURE. */
  33. void *
  34. __go_allocate_trampoline (uintptr_t size, void *closure)
  35. {
  36. uintptr_t ptr_size;
  37. uintptr_t full_size;
  38. unsigned char *ret;
  39. /* Because the garbage collector only looks at aligned addresses, we
  40. need to store the closure at an aligned address to ensure that it
  41. sees it. */
  42. ptr_size = sizeof (void *);
  43. full_size = (((size + ptr_size - 1) / ptr_size) * ptr_size);
  44. full_size += ptr_size;
  45. runtime_lock (&trampoline_lock);
  46. if (full_size < trampoline_page_size - trampoline_page_used)
  47. trampoline_page = NULL;
  48. if (trampoline_page == NULL)
  49. {
  50. uintptr_t page_size;
  51. unsigned char *page;
  52. page_size = getpagesize ();
  53. __go_assert (page_size >= full_size);
  54. page = (unsigned char *) runtime_mallocgc (2 * page_size - 1, 0, 0, 0);
  55. page = (unsigned char *) (((uintptr_t) page + page_size - 1)
  56. & ~ (page_size - 1));
  57. #ifdef HAVE_SYS_MMAN_H
  58. {
  59. int i;
  60. i = mprotect (page, page_size, PROT_READ | PROT_WRITE | PROT_EXEC);
  61. __go_assert (i == 0);
  62. }
  63. #endif
  64. trampoline_page = page;
  65. trampoline_page_size = page_size;
  66. trampoline_page_used = 0;
  67. }
  68. ret = trampoline_page + trampoline_page_used;
  69. trampoline_page_used += full_size;
  70. runtime_unlock (&trampoline_lock);
  71. __builtin_memcpy (ret + full_size - ptr_size, &closure, ptr_size);
  72. return (void *) ret;
  73. }
  74. /* Scan the trampoline page when running the garbage collector. This
  75. just makes sure that the garbage collector sees the pointer in
  76. trampoline_page, so that the page itself is not freed if there are
  77. no other references to it. */
  78. void
  79. runtime_trampoline_scan (void (*addroot) (Obj))
  80. {
  81. if (trampoline_page != NULL)
  82. addroot ((Obj){(byte *) &trampoline_page, sizeof trampoline_page, 0});
  83. }