$ uname -srvp
Linux 3.13.0-49-generic #83-Ubuntu SMP Fri Apr 10 20:11:33 UTC 2015 x86_64
$ cat /proc/cpuinfo | grep 'model name'
model name : Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz
model name : Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz
model name : Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz
model name : Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz
import std.stdio;
void main()
{
"Hello!".writeln;
}
$ dub build
$ ls -l tinybin
-rwxrwxr-x 1 kubo39 kubo39 1584546 5月 2 16:31 tinybin
$dub build --build=release
$ ls -l tinybin
-rwxrwxr-x 1 kubo39 kubo39 700884 5月 2 16:35 tinybin
$ git clone git://github.com/kubo39/syscall.d
$ dub add-local syscall.d ~master
dub.json
の dependencies に syscall.d を追加する.
{
"name": "tinybin",
"description": "A minimal D application.",
"copyright": "Copyright © 2015, kubo39",
"authors": ["kubo39"],
"dependencies": {
"syscall.d": {"version": "~master", "path": "../syscall.d"}
}
}
app.d
を syscall.d を使ったコードに変更
import syscall : syscall, WRITE;
void main()
{
auto hello = "Hello!\n";
size_t stdout = 1;
syscall(WRITE, stdout, cast(size_t) hello.ptr, hello.length);
}
dub build --build=release
でビルド.
$ ls -l tinybin
-rwxrwxr-x 1 kubo39 kubo39 429067 5月 2 16:56 tinybin
import syscall : syscall, WRITE;
void main()
{
immutable(char)[7] hello = "Hello!\n";
size_t stdout = 1;
syscall(WRITE, stdout, cast(size_t) hello.ptr, 7);
}
dub build --build=release
でビルド.
$ ls -l tinybin
-rwxrwxr-x 1 kubo39 kubo39 429037 5月 2 17:02 tinybin
$ strip tinybin
$ wc -c < tinybin
285536
readelf -h tinybin
でセクションヘッダの開始位置を調べる.
$ dd if=tinybin of=tinybin_nosectionhdr count=283487 bs=1
$ chmod +x tinybin_nosectionhdr
$ wc -c < tinybin_nosectionhdr
283487
syscall.d
の syscall 呼び出し部分を直接コードに書く.
@system:
void write(size_t p, size_t len)
{
synchronized asm
{
mov RAX, 1; // WRITE
mov RDI, 1; // STDOUT
mov RSI, p[RBP];
mov RDX, len[RBP];
syscall;
}
}
void main()
{
immutable(char)[7] hello = "Hello!\n";
write(cast(size_t) hello.ptr, 7);
}
こういう build.sh
を用意.
#!/bin/bash
set -e
for d in dmd; do
which $d >/dev/null || (echo "Can't find $d, needed to build"; exit 1)
done
dmd | head -1
echo
set -x
dmd -c -noboundscheck -release source/app.d
gcc app.o -o tinybin -s -m64 -L/usr/lib/x86_64-linux-gnu -Xlinker -l:libphobos2.a -lpthread
ビルドする.
$ ./build.sh
DMD64 D Compiler v2.067.1
+ dmd -c -noboundscheck -release source/app.d
+ gcc app.o -o tinybin -s -m64 -L/usr/lib/x86_64-linux-gnu -Xlinker -l:libphobos2.a -lpthread
$ ./tinybin
Hello!
$ wc -c < tinybin
170848
6 のとこででやったように dd 使って削る.
$ dd if=tinybin of=tinybin_nosectionhdr count=168799 bs=1
$ chmod +x tinybin_nosectionhdr
$ ./tinybin_nosectionhdr
Hello!
$ wc -c < tinybin_nosectionhdr
168799
この程度のプログラムで特にチェックする必要はなさそう.
@system:
void write(size_t p, size_t len)
{
asm
{
mov RAX, 1; // WRITE
mov RDI, 1; // STDOUT
mov RSI, p[RBP];
mov RDX, len[RBP];
syscall;
}
}
void main()
{
immutable(char)[7] hello = "Hello!\n";
write(cast(size_t) hello.ptr, 7);
}
ビルドする.
$ wc -c < tinybin
170760
$ dd if=tinybin of=tinybin_nosectionhdr count=168711 bs=1
$ chmod +x tinybin_nosectionhdr
$ wc -c < tinybin_nosectionhdr
168711
$ ./tinybin_nosectionhdr
Hello!
実行もできてる.
@system:
extern(C)
{
void write(size_t p, size_t len)
{
asm
{
mov RAX, 1; // WRITE
mov RDI, 1; // STDOUT
mov RSI, p[RBP];
mov RDX, len[RBP];
syscall;
}
}
int main()
{
immutable(char)[7] buf = "Hello!\n";
write(cast(size_t)buf.ptr, 7);
return 0;
}
}
$ ./tinybin
Hello!
$ wc -c < tinybin
134872
$ dd if=tinybin of=tinybin_nosectionhdr count=132887 bs=1
$ wc -c < tinybin_nosectionhdr
132887
リンカのオプションに -e main -Xlinker --gc-section
追加.
#!/bin/bash
set -e
for d in dmd; do
which $d >/dev/null || (echo "Can't find $d, needed to build"; exit 1)
done
dmd | head -1
echo
set -x
dmd -c -noboundscheck -release source/app.d
gcc app.o -o tinybin -e main -s -Xlinker --gc-section -l:libphobos2.a -lpthread
exit(2)
呼び出しを追加. (segv対策)
@system:
extern(C)
{
void write(size_t p, size_t len)
{
asm
{
mov RAX, 1; // WRITE
mov RDI, 1; // STDOUT
mov RSI, p[RBP];
mov RDX, len[RBP];
syscall;
}
}
void exit()
{
asm{
mov RAX, 60; // EIXT
mov RDI, 0;
syscall;
}
}
int main()
{
immutable(char)[7] buf = "Hello!\n";
write(cast(size_t)buf.ptr, 7);
exit();
return 0;
}
}
ビルド.
$ ./build.sh
DMD64 D Compiler v2.067.1
+ dmd -c -noboundscheck -release source/app.d
+ gcc app.o -o tinybin -e main -s -Xlinker --gc-section -l:libphobos2.a -lpthread
$ ./tinybin
Hello!
$ wc -c < tinybin
77352
さらにsection headerを削った場合.
$ dd if=tinybin of=tinybin_nosectionhdr count=75495 bs=1
$ ./tinybin_nosectionhdr
Hello!
$ wc -c < tinybin_nosectionhdr
75495
@system:
void write(size_t p, size_t len)
{
asm
{
mov RAX, 1; // WRITE
mov RDI, 1; // STDOUT
mov RSI, p[RBP];
mov RDX, len[RBP];
syscall;
}
}
void exit()
{
asm
{
mov RAX, 60; // EXIT
mov RDI, 0;
syscall;
}
}
void main()
{
immutable(char)[7] buf = "Hello!\n";
write(cast(size_t) buf.ptr, 7);
exit();
}
エントリポイントを差し替えてビルド.
$ ./build.sh
DMD64 D Compiler v2.067.1
+ dmd -c -noboundscheck -release source/app.d
+ gcc app.o -o tinybin -e _Dmain -s -Xlinker --gc-section -l:libphobos2.a -lpthread
$ ./tinybin
Hello!
$ wc -c < tinybin
77336
dd版.
$ dd if=tinybin of=tinybin_nosectionhdr count=75479 bs=1
$ wc -c < tinybin_nosectionhdr
75479
sectionをひとまとめにしてalignmentをなくすlinker scriptを書く.
SECTIONS {
. = 0x400078;
combined . : AT(0x400078) ALIGN(1) SUBALIGN(1) {
*(.text*)
*(.data*)
*(.rodata*)
*(.bss*)
}
}
0x400078
は用意したカスタムヘッダの最後に積まれるアドレス.
ld が _Dmain
をファイルのどこに置くかわからないので、別々にバイナリを生成する必要がある.
_Dmain
の置かれるアドレス領域は payload
を生成するタイミングで決定する.
objdump の -j オプションを使って script.ld の combined section のみを抽出したバイナリ payload.bin を作成する.
用意したカスタムELFヘッダ(一切の命令が入ってなくてdata filedのみある)を使って、生成した payload.bin とくっつける.
その際に _Dmain
のエントリポイント(プログラムの開始位置)を教えてやる.
最終的にこうなる.
$ ./build.sh
DMD64 D Compiler v2.067.1
+ dmd -c -noboundscheck -release source/app.d
+ gcc app.o -o payload -e _Dmain -T script.ld -s -Xlinker --gc-section -l:libphobos2.a -lpthread
+ objcopy -j combined -O binary payload payload.bin
++ nm -f posix payload
++ grep _Dmain
++ awk '{print $3}'
+ ENTRY=00000000004000c8
+ nasm -f bin -o tinybin -D entry=0x00000000004000c8 elf.s
$ ./tinybin
Hello!
$ wc -c < tinybin
296