考虑这个 C 代码(具有实现定义的行为)
void foo()
{
struct struct1* s1 = (struct struct1*)0x01234567; /* exp1 */
struct struct2* s2 = (struct struct2*)0x01234567; /* exp2 */
struct struct3* s3 = (struct struct3*)0x01234567; /* exp3 */
float* f = (float*)0x01234567; /* exp4 */
int* i = (int*)0x01234567; /* exp5 */
char* c = (char*)0x01234567; /* exp6 */
}
为了清楚起见,假设s1位于[rsp-08h],那么exp1组装为
mov QWORD [rsp-08h], 1234567h
为了清楚起见,假设s2位于[rsp-10h],那么exp2组装为
mov QWORD [rsp-10h], 1234567h
为了清楚起见,假设s3位于[rsp-18h],那么exp3组装为
mov QWORD [rsp-18h], 1234567h
为了清楚起见,假设f位于[rsp-20h],那么exp4组装成
mov QWORD [rsp-20h], 1234567h
为了清楚起见,假设i 位于[rsp-28h],那么exp4 组装为
...haven't you got it yet?
在汇编中没有类型这样的东西,因此也没有强制转换这样的东西。
汇编中只有数据,这就是为什么我们发明了类型化的高级语言,不是为了ifs 也不是为了fors,而是为了类型检查。
如果您想做struct some_struct_t *s1 = (struct some_struct_t *)some_buffer;,则转换为s1 = some_buffer。
这只是赋值。
现在,由于some_buffer 是一个具有自动存储功能的数组,并且在 x86 上翻译为“它在堆栈上”,您可能想知道 struct some_struct_t *s1 = (struct some_struct_t *)some_buffer; 的语义到底是什么除了(人工)演员表 正如你现在所知,它只在编译过程中存在。
You surely know that some_buffer decay into a pointer to the first element,那么在翻译该指令时,唯一难做的事情就是找出第一个元素的地址。
好吧,我不能告诉你太多,因为我不知道你把第一个项目放在哪里,但总的来说 some_buffer 在堆栈上,所以一旦调整,这个就可以了
;Compute the address of the first element
lea rax, [rsp+...] ;or [rbp-...] if a frame pointer is available
;Store it in a local var
mov QWORD [rsp+...], rax ;As above, also you can use any other scratch reg
第一个省略号用于代替偏移量,相对于数组第一项的rsp。
第二个用于代替s1 指针的偏移量。
对于live example see here。
请注意,在该示例中,GCC 过于热衷于使用红色区域,但我们可以原谅他,因为我必须禁用 any 优化才能进行合理的反汇编。
如果您想知道如何为malloc 做同样的事情,那么如果您仍然不想使用godbolt.org,解决方案就在这里
mov edi, 1024
call malloc
mov QWORD [rsp+...], rax