0%

看慣了x86的Intel Syntax,對於AT&T的語法還是不習慣。
使用以下方式可以將gcc編譯出來的Code變成Intel Syntax。

1
$ gcc test.c -S -masm=intel

這只支援gcc,clang不行。

突然想到,順手把他記下來吧。
在C++當中,如果要自己定義++和–這兩個operator,需要不同的Function signature。

1
2
3
4
5
6
7
8
class Date {
//...
public:
Date& operator++(); //prefix
Date& operator--(); //prefix
Date& operator++(int unused); //postfix
Date& operator--(int unused); //postfix
};

最大的差別就在於Postfix裡面有個未使用的參數。至於為什麼要這麼設計,可以參考這裡

這種技巧很常在Open Source的專案中看到,看了Arrays of Length Zero原本以為是GCC獨有Extensionm,後來發現Clang跟VC都能使用,記錄一下。

1
2
3
4
5
6
7
8
9
10
11
12
struct datapacket
{
int size;
int data[0];
];
void f()
{
struct datapacket *pkt =
malloc( sizeof(struct datapacket) +
sizeof(int)*9 );
/* further code . . . */
}

這邊的data[0]不佔記憶體空間,因此sizeof(struct datapacket)大小相當於sizeof(int),而data就是一個記憶體指標,指向一塊連續的記憶體。

一直想寫一篇關於LLVM的文章,想了好久終於下筆了。 同樣從Hello world開始下手

1
2
3
4
5
6
#include <stdio.h>
int main()
{
puts("Hello world!\n");
return 0;
}

編譯成 LLVM的IR中間形式,利用lli來執行IR Code。

1
2
$ clang hello.c -S -emit-llvm -o hello.lli
$ lli hello.lli

刪除hello.lli的Meta data之後,一個Hello world的最小部分掌這個樣子

1
2
3
4
5
6
7
8
@.str = private unnamed_addr constant [14 x i8] c"Hello world!\0A\00", align 1
define i32 @main() #0 {
%1 = alloca i32, align 4
store i32 0, i32* %1
%2 = call i32 @puts(i8* getelementptr inbounds ([14 x i8]* @.str, i32 0, i32 0))
ret i32 0
}
declare i32 @puts(i8*) #1

這篇文章正好是上一篇的相反行為。為了加速某些特殊運算,有時需要呼叫C/C++的程式碼。使用Python內建的ctypes可以幫助完成這件事。

lang: cpp foo.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>

class Foo {
public:
void bar() {
std::cout << "Hello" << std::endl;
}
};

extern "C" {
Foo* Foo_new(){ return new Foo(); }
void Foo_bar(Foo* foo) { foo->bar(); }
}

將其編譯成Shared Library / DLL。

1
2
$ g++ -c -fPIC foo.cpp -o foo.o
$ g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o

接著就是Python code上場了

1
2
3
4
5
6
7
8
9
10
11
12
from ctypes import cdll
lib = cdll.LoadLibrary('./libfoo.so')

class Foo(object):
def __init__(self):
self.obj = lib.Foo_new()

def bar(self):
lib.Foo_bar(self.obj)

f = Foo()
f.bar() #and you will see "Hello" on the screen

Windows版跟Linux版大同小異,最大的差別在於Windows可以寫 ./libfoo 而不用寫 ./libfoo.dll:而 Linux沒有附檔名的話會發生錯誤。

當然,swigBoost Python一樣可以幫忙完成這件事。

在Mint 15下測試,Python版本2.7.3。以下的操作步驟跟Python相關,有需要的話需要修改。
首先,先安裝python-dev

1
$ apt-get install python-dev

測試的Python code cal.py

1
2
3
4
def mix(a, b) :  
r1 = a + b
r2 = a - b
return (r1, r2)

C語言本體:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include "Python.h"
#include <string>
using namespace std;

int main(int argc, char * argv[])
{
string filename = "cal"; // cal.py
string methodname_mix = "mix"; // function name

Py_Initialize();
PyRun_SimpleString ("import sys; sys.path.insert(0, '.')");

// load the module
PyObject * pyFileName = PyString_FromString(filename.c_str());
PyObject * pyMod = PyImport_Import(pyFileName);

// load the function
PyObject * pyFunc_mix = PyObject_GetAttrString(pyMod, methodname_mix.c_str());

// test the function is callable
if (pyFunc_mix && PyCallable_Check(pyFunc_mix))
{
PyObject * pyParams = PyTuple_New(2);
PyTuple_SetItem(pyParams, 0, Py_BuildValue("i", 5));
PyTuple_SetItem(pyParams, 1, Py_BuildValue("i", 2));

// ok, call the function
int r1 = 0, r2 = 0;
PyObject * pyValue = PyObject_CallObject(pyFunc_mix, pyParams);
PyArg_ParseTuple(pyValue, "i|i", &r1, &r2);
if (pyValue)
{
printf("%d,%d\n", r1, r2); //output is 7,3
}
}

// Clean up
Py_DECREF(pyMod);
Py_DECREF(pyFileName);

Py_Finalize();

return 0;
}

編譯他

1
$ g++ pythontest.cpp -o pythontest -I/usr/include/python2.7 -lpython2.7

其中值得注意的是PyRun_SimpleString ("import sys; sys.path.insert(0, '.')");這行。這樣的話他才會在目前目錄下找cal.py這個檔案,不然會尋找/usr/lib/python2.7這個目錄。找不到會Segmentation fault。
cal.py會先被編譯成cal.pyc,之後只需要這檔案即可。

而在Visual Studio使用的時候,要注意以下情況

  • 假設你是用x86版的Python,只能使用Win32版的Setting,不然就只能使用x64版。
  • 在Windows下需要PyRun_SimpleString ("import sys; sys.path.insert(0, '.')");即可找到目錄下的cal.py。當然,放到Python27\Lib也是可以。

如果要讓事情變得更簡單,SWIG或是Boost.Python都是不錯的解決方案。

參考文章

在網路上看到,隨手記錄起來。

explainshell,在搜尋框輸入Linux command,接會parsing command,從Ubuntu的manpage library中找出說明文件。 GitHub找得到Source code。 類似的服務還有cdecl將C語言的宣告樣式翻譯成白話英文。以及Regexper將Regular Expression轉化成圖形表示式。

在gdb 7.0之後加入的Reverse debugging,終於有時間可以玩玩看了。
同樣的,來個程式範例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

void foo() {
printf("inside foo()");
int x = 6;
x += 2;
}

int main() {
int x = 0;
x = x + 2;
foo();
printf("x = %d\n", x);
x = 4;
return 0;
}

編譯它

1
gdb -g -o test test.c

使用gdb來玩玩看,最簡單的用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ gdb test
(gdb) start
Temporary breakpoint 1, main () at test.c:10
10 int x = 0;
(gdb) record
(gdb) next
11 x = x + 2;
(gdb) disp x
1: x = 0
(gdb) next
12 foo();
1: x = 2
(gdb) reverse-next
11 x = x + 2;
1: x = 0

在使用之前,要先用record記錄操作順序,才有機會還原狀態。不用的時候可以使用record stop停止。
接著常用的step, next, stepi跟nexti都有逆向版。可以參考GDB and Reverse Debugging

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(gdb) b 15
Breakpoint 2 at 0x400578: file test.c, line 15.
(gdb) continue
Continuing.
inside foo()x = 2

Breakpoint 2, main () at test.c:15
15 return 0;
(gdb) b foo
Breakpoint 3 at 0x400524: file test.c, line 4.
(gdb) reverse-continue
Continuing.

Breakpoint 3, foo () at test.c:4
4 printf("inside foo()");

想要追蹤之前執行過的中斷點該怎麼辦? 使用reverse-continue
也可以用watchpointer來追蹤到底如何被修改的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(gdb) set can-use-hw-watchpoints 0
(gdb) watch x
Watchpoint 3: x
(gdb) continue
Continuing.
Watchpoint 3: x

Old value = 0
New value = 2
(gdb) reverse-continue
Continuing.
Watchpoint 3: x

Old value = 2
New value = 0
main () at test.c:11
11 x = x + 2;

更多的gdb使用教學,可以參考