<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/131502>131502</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
[compiler-rt] BlocksRuntime `_Block_dump` function doesn't dump current ABI
</td>
</tr>
<tr>
<th>Labels</th>
<td>
new issue
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
ADKaster
</td>
</tr>
</table>
<pre>
The helper method here:
https://github.com/llvm/llvm-project/blob/508db53d1af5b01f8f8275229f087bb6407f0033/compiler-rt/lib/BlocksRuntime/runtime.c#L620-684
Prints that the compiler is "obsolete" on current clang.
The following test program prints the following with both Apple clang version 16.0.0 (clang-1600.0.26.6) and
Homebrew clang version 19.1.7:
```
Block compiled by obsolete compiler, please recompile source for this Block
closure flags: 0x42000000
^0x16bbcf3c0 (new layout) =
isa: stack Block
flags: HASHELP
refcount: 0
invoke: 0x104233cb8
descriptor: 0x104234058
descriptor->reserved: 0
descriptor->size: 40
descriptor->copy helper: 0x104233ce8
descriptor->dispose helper: 0x104233d20
x=1
```
<details>
<summary>Program source, with most contents copied from BlocksRuntime/runtime.c</summary>
```c++
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
struct Block_descriptor {
unsigned long int reserved;
unsigned long int size;
void (*copy)(void *dst, void *src);
void (*dispose)(void *);
};
struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
};
enum {
BLOCK_REFCOUNT_MASK = (0xffff),
BLOCK_NEEDS_FREE = (1 << 24),
BLOCK_HAS_COPY_DISPOSE = (1 << 25),
BLOCK_HAS_CTOR = (1 << 26), /* Helpers have C++ code. */
BLOCK_IS_GC = (1 << 27),
BLOCK_IS_GLOBAL = (1 << 28),
BLOCK_HAS_DESCRIPTOR = (1 << 29)
};
extern "C" void * _NSConcreteStackBlock[32];
extern "C" void * _NSConcreteMallocBlock[32];
extern "C" void * _NSConcreteAutoBlock[32];
extern "C" void * _NSConcreteFinalizingBlock[32];
extern "C" void * _NSConcreteGlobalBlock[32];
extern "C" void * _NSConcreteWeakBlockVariable[32];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
const char *_Block_dump(const void *block);
const char *_Block_dump(const void *block) {
struct Block_layout const*closure = (struct Block_layout const*)block;
static char buffer[512];
char *cp = buffer;
if (closure == NULL) {
sprintf(cp, "NULL passed to _Block_dump\n");
return buffer;
}
if (! (closure->flags & BLOCK_HAS_DESCRIPTOR)) {
printf("Block compiled by obsolete compiler, please recompile source for this Block\n");
printf("closure flags: 0x%08x\n", closure->flags);
//exit(1);
}
cp += sprintf(cp, "^%p (new layout) =\n", (void *)closure);
if (closure->isa == NULL) {
cp += sprintf(cp, "isa: NULL\n");
}
else if (closure->isa == _NSConcreteStackBlock) {
cp += sprintf(cp, "isa: stack Block\n");
}
else if (closure->isa == _NSConcreteMallocBlock) {
cp += sprintf(cp, "isa: malloc heap Block\n");
}
else if (closure->isa == _NSConcreteAutoBlock) {
cp += sprintf(cp, "isa: GC heap Block\n");
}
else if (closure->isa == _NSConcreteGlobalBlock) {
cp += sprintf(cp, "isa: global Block\n");
}
else if (closure->isa == _NSConcreteFinalizingBlock) {
cp += sprintf(cp, "isa: finalizing Block\n");
}
else {
cp += sprintf(cp, "isa?: %p\n", (void *)closure->isa);
}
cp += sprintf(cp, "flags:");
if (closure->flags & BLOCK_HAS_DESCRIPTOR) {
cp += sprintf(cp, " HASDESCRIPTOR");
}
if (closure->flags & BLOCK_NEEDS_FREE) {
cp += sprintf(cp, " FREEME");
}
if (closure->flags & BLOCK_IS_GC) {
cp += sprintf(cp, " ISGC");
}
if (closure->flags & BLOCK_HAS_COPY_DISPOSE) {
cp += sprintf(cp, " HASHELP");
}
if (closure->flags & BLOCK_HAS_CTOR) {
cp += sprintf(cp, " HASCTOR");
}
cp += sprintf(cp, "\nrefcount: %u\n", closure->flags & BLOCK_REFCOUNT_MASK);
cp += sprintf(cp, "invoke: %p\n", (void *)(uintptr_t)closure->invoke);
{
struct Block_descriptor *dp = closure->descriptor;
cp += sprintf(cp, "descriptor: %p\n", (void *)dp);
cp += sprintf(cp, "descriptor->reserved: %lu\n", dp->reserved);
cp += sprintf(cp, "descriptor->size: %lu\n", dp->size);
if (closure->flags & BLOCK_HAS_COPY_DISPOSE) {
cp += sprintf(cp, "descriptor->copy helper: %p\n", (void *)(uintptr_t)dp->copy);
cp += sprintf(cp, "descriptor->dispose helper: %p\n", (void *)(uintptr_t)dp->dispose);
}
}
return buffer;
}
#pragma clang diagnostic pop
int main()
{
__block int x = 0;
auto b = ^{
x++;
};
printf("%s", _Block_dump(b));
b();
printf("x=%d\n", x);
}
```
</details>
As for *why* I want an accurate dump method, I'm trying to add support to my own std::function-like type erased function for blocks.
I actually want to inspect the pointed-to Block_layout so that I can scan the captured variables for GC pointers in my naive mark-and-sweep conservative GC. And it doesn't seem like I've got a full grasp on which bytes to look for GC pointers in based on my manual debugging.
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJy0WV9v27oO_zTqC9FAluP8eeiDmyZbsW4t1p17cJ8C2ZYT3cqSIclpcj79hWQ7sdM0W7OdItgSSyR_pEiapKgxfCUZu0HRLYrurmhl10rfxHdfqLFMXyUq2938WDNYM1EyDQWza5XBmmmGwhhh91lbWxr3iywQWay4XVfJIFUFIgshNu1_16VW_2OpRWSRCJUgsojwJEuiMAtoHiU4yCf5hIwjQqY5noyTZDTE4xzjMERkkaqi5ILpa-3oBXfkt0KlL-Z7JS0vGCILXX8bpIiEDyOCr0eTYQ3wSXNpDdg1tWDXDFpuwA0gQlRilGCWIUJASUgrrZm0kAoqV4Oag7NAroRQr1yuwDJjodRqpWkBZcu8u-OV2zUkyq4hLkvBal6wYdpwJSEYDfAAAyIT__w6GGE8wAMyGowQmQKVGSAcf1YFSzR7PSaeDoLBeG98NMLNB8feIq12GSQ7aFXbq4zIDErBqGGgWfMQjKp06uBrsGtuwPNBOE6FMpVmkAu6cgcMeDsk2P85ydEcb4NRkqR5mHptJHsFQXeqsk4NFN4hHHNDHaWxNH3ZM94z_Bw_f54_PCEca5anqpLWi3F0cqNeWC00wEMShmkyQTjOmEk1L63SnbUhjvpr1yica2aY3rCs5dhfNfwfz334dilV5a7x95589lZGxk2pDDuxOyOO8RaFd8HRKblPOMuYpVwYFM73j0xVFFTvUDh_anyrPhh3Zt6hCmUspEpa5jwuVSVnGeRaFfB-LIQzRBYHzkdOkyJy6z44RiTkMhVVxsBBsRlXg3VDcLyUsfzdNS7tfg3HxuoqtTW85cF0gMZOJgBAJX0CykAouQIuLRzO7f099em16xvFM-d_iMTu7BCZIjJpHsaZsc6A7U-jU7d-irY5zD75fjMa3zVfjtSqPb6jUkvqXb996FA3bt999FbZDqAmBo7wzGAwGPR0eNfKpBcu7Xafp2O4L0qlLctgQzWniWBmUAtYHGnLZFW06t0-PM6-LL_PF7PHv779WH6Nn7-4QIea7wRv8zzPPeJZI62m-Daf3z0vF9_n8_32miJwfoPCGZDhCbLP8fNy9vj03-Xd_fPT43ND3COL3iP78fjdb-_tHtW7Wxt89nFrYE03DGZ1MECqMtYxxYHt_fPy06yL_60S4xNoHNnD42380CPtkU3eUeJu_jz7fv90UpWpozk6qa1lWrp32sy9zFqfgeW355mSqWaWPbtEXOfh6DYkKGqof4H0KxVCpZfRxpVVl1EuuKSC_8Pl6jL6T0IlVFxG-zejta3-04RIn0Od_0pNVwVt3tIZpyupjOUplJVZn9_BV1Jp5qSS678zVmqWUsuy64ylgmpquZIGEVJLSpV02X9NfVwvm2CvitKVEX6t1SDxurYJ4uN0nVR2KtF5Ipdqm-qgccxzWxGZ1sw7KYs6C3hYSZXnTKPoNgoOtnWbWtBp6YU0-w75M69LqD0Mt-nbXw8PfR28OF-l5W57WYc_cRuhpMawDKyCrmGimXRm76ZY96eZrbR8A8MFYBcQIkEHl6sSfN4HREYnI9uH_jHgPV5EyJ8t646V60o6UfAhEuHJdk81g2O93pipbgPYlluXrg7LBzu58yS37rROnAuK5ohE5el68gCj_4puQB2E9XzDYeWGnneRs6CaKtZTHhvwoBcThp2VfDoPXwSlW1Cf8NfLQHUz_EWoCs8A1oyWfxrb4Q1yQPYrkD7N_hU43dfKRaZaeQZ_Gtbx2_IiaPmeyS_D-6iQhZPj4vxnId2o-o7ss4LaLHYM_Y0pf5qfP6Ke62m7xO-Z7acoDiXzr3o8uM1f578h01e4H1X4_tnRXCzzuMa_wN5-hvCbCC476dnZM_7Ji24mu1MPRKLqzHu2g7fXfvVEnw-8_UjlbOAhMqm4tKXVS3sUh_t-tKOr_3auA62Ltw6bUz3pT8H35z5nFcjKNzXJL7I-HhshEonumWRlb8dvSGnHT6cl-NX94OEg4A_E0gdxHs3CPuI4tSrtQCb8DRBvp2wfx9EZ7hyXbYdvJyr8evFcm6fK-pC4tFBQLlHdzTvSVufl0rc_fuKz9eGADwahlVWQ1G1UNO-f1raZz_Vyy94ruqU7IpFprNHv8ZK6v-j5UtJg7D7rMtu6woJEWcfE294g7NREE5HF8VAzNr77QCR-Xe_8zAleqbRAJdA0rTS1DBzM5lrByblHZFyA1Ts_aFdAswxMVZZKW_ez2IF6lWCsi08UxnklU9cnXwv-wsDuSgZMU9fTtSsegTe_aYb590BTW1EhdjUaq4BLU7K0vh0oFZeuC7eq38waVd8g3ENKJRj3j79MoKWtdHeM5iV-mjWMtAEuHW5J-YZBQfXLNZXZtXllrPQdMtMbat3ap9kAYpkBt5ApZiQiYwuGsQK8ds40GwYrZYFCXgkBK01NCUrC65qna0h2lhmnjlDq5RSKxFtGeTgFlRUVkLGkWq14fdNxld2E2TSc0it2E4yHJMDReIKv1jcZwzhIslEwJMEQk3E-DNMAD0OahGmWpNMrfkMwiXAYjPAoGBE8CIIgi4bTyXQaUZLjEA0xKygXAyE2xUDp1RU3pmI3QRhEmFwJmjBh_C0UIa7v86v-LXl3pW_8BVJSrQwaYsGNNQc2llvhr6-6d0TRXX8kDmiEu2Exwgf_OJjau2J7_RPf3l9VWtx8-HbLIzeILBrVNjfk_wEAAP__l-rs0Q">