C言語の型キャストの内部
単に4バイトの変数の領域を8バイトで見る、などの実装は無理そう
メモリの確保を行っている?
intのキャストを行うプログラム
code:other.c
int main(void)
{
int n = 5;
printf("%lld\n", (long long)n);
return 0;
}
code:other.asm
.file "other.c"
.intel_syntax noprefix
.text
.section .rodata
.LC0:
.string "%lld\n"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
endbr64
push rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
mov rbp, rsp
.cfi_def_cfa_register 6
sub rsp, 16
cdqe
mov rsi, rax
mov rdi, rax
mov eax, 0
call printf@PLT
mov eax, 0
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
.align 8といった行はどういう意味?
重要なのは、アセンブラへの命令としてこれが書かれているということ
nasmではsection .text
このアセンブリでは.section .text
この質問、回答、コメントによると、
例
必要そうな部分は以下
code:asm-part.asm
push rbp
mov rbp, rsp
sub rsp, 16
cdqe
mov rsi, rax
mov rdi, rax
mov eax, 0
call printf@PLT
スタックフレームの底のポインタがあるから、ローカル変数をセットする cdqeの時点でeaxに何が入っているのかを見たい
と思ったが、disasしてこれは変数nの初期値 = 5だと気づいた
コメントも入れている
code:gdb.asm
Dump of assembler code for function main:
0x0000555555555149 <+0>: endbr64
0x000055555555514d <+4>: push rbp
0x000055555555514e <+5>: mov rbp,rsp
=> 0x0000555555555151 <+8>: sub rsp,0x10
0x0000555555555155 <+12>: mov DWORD PTR rbp-0x4,0x5 # 変数 n 0x000055555555515c <+19>: mov eax,DWORD PTR rbp-0x4 # n の値をeaxに 0x000055555555515f <+22>: cdqe
0x0000555555555161 <+24>: mov rsi,rax
0x0000555555555164 <+27>: lea rax,rip+0xe99 # 0x555555556004 0x000055555555516b <+34>: mov rdi,rax
0x000055555555516e <+37>: mov eax,0x0
0x0000555555555173 <+42>: call 0x555555555050 <printf@plt>
0x0000555555555178 <+47>: mov eax,0x0
0x000055555555517d <+52>: leave
0x000055555555517e <+53>: ret
これで本当に正しい?
printfに必要なのはアドレスで、アドレスは上記のABIでは整数と同じように渡される $ readelf -a ./other
今回はotherというファイル名でコンパイルしている
code:readelf.txt
OS/ABI: UNIX - System V
If the class is INTEGER, the next available register of the sequence %rdi, %rsi, %rdx, %rcx, %r8 and %r9 is used13
この記述から、rdiが第一引数(=%lld\n)、rsiが第二引数(=n=5)
あれ?t6o_o6t.icon
キャストする処理は現れなかった
mov rsi raxで自然と64ビット?
ソースコードからキャストする部分を抜いてコンパイル
code:gdb.asm
Dump of assembler code for function main:
0x0000555555555149 <+0>: endbr64
0x000055555555514d <+4>: push rbp
0x000055555555514e <+5>: mov rbp,rsp
=> 0x0000555555555151 <+8>: sub rsp,0x10
0x0000555555555155 <+12>: mov DWORD PTR rbp-0x4,0x5 0x000055555555515c <+19>: mov eax,DWORD PTR rbp-0x4 0x000055555555515f <+22>: mov esi,eax
0x0000555555555161 <+24>: lea rax,rip+0xe9c # 0x555555556004 0x0000555555555168 <+31>: mov rdi,rax
0x000055555555516b <+34>: mov eax,0x0
0x0000555555555170 <+39>: call 0x555555555050 <printf@plt>
0x0000555555555175 <+44>: mov eax,0x0
0x000055555555517a <+49>: leave
0x000055555555517b <+50>: ret
プロセッサの防御機構のようなものだと予想していたが、ビット数にも関連している?t6o_o6t.icon In 64-bit mode, the default operation size is the size of the destination register. Use of the REX.W prefix promotes this instruction (CDQE when promoted) to operate on 64-bit operands. In which case, CDQE copies the sign (bit 31) of the doubleword in the EAX register into the high 32 bits of RAX.
前後の解説を読んで、この命令は符号つきのレジスタの値を符号拡張する命令だと考えた Double the size of the source operand by means of sign extension. The CBW (convert byte to word) instruction
copies the sign (bit 7) in the source operand into every bit in the AH register. The CWDE (convert word to doubleword) instruction copies the sign (bit 15) of the word in the AX register into the high 16 bits of the EAX register
以下のように推測t6o_o6t.icon
キャストをする
1. nが格納されたeaxレジスタはCDQE命令でraxに符号を維持したまま拡張される
2. 以降nはrax(64bit)で参照される
キャストをしない
nはeax(32bit)で参照される