momo zone

调核人的blog

mplayer compile error (svn 2011)

最近的mplayer 编译总是出错,准确的说svn2011 版本的基本都无法正常编译。就连1.0RC4 都不行。

In file included from h264.h:33:0,
                 from h264_cabac.c:33:
cabac.h: In function ‘get_cabac_noinline’:
cabac.h:527:5: error: can’t find a register in class ‘GENERAL_REGS’ while reloading ‘asm’
cabac.h:527:5: error: ‘asm’ operand has impossible constraints
问题发生在cabac.h:
 __asm__ volatile(
        “movl “RANGE    “(%2), %%esi            \n\t”
        “movl “LOW      “(%2), %%ebx            \n\t”
        BRANCHLESS_GET_CABAC(“%0”, “%2”, “(%1)”, “%%ebx”, “%%bx”, “%%esi”, “%%edx”, “%%dl”)
        “movl %%esi, “RANGE    “(%2)            \n\t”
        “movl %%ebx, “LOW      “(%2)            \n\t”
        :”=&a”(bit)
        :”r”(state), “r”(c)
        : “%”REG_c, “%ebx”, “%edx”, “%esi”, “memory”
goo 了一下发现这个问题很常见了,好像libavcode 较新版本都有这个问题。
解决方法是给CFLAG 加参数 -fomit-frame-pointer。 这样问题的确解决了,探究一下这个参数,顺便温习一下c函数的汇编过程。
GCC手册上面这么说:

Don’t keep the frame pointer in a register for functions that don’t need one. This avoids the instructions to save, set up and restore frame pointers; it also makes an extra register available in many functions. It also makes debugging impossible on some machines.

On some machines, such as the VAX, this flag has no effect, because the standard calling sequence automatically handles the frame pointer and nothing is saved by pretending it doesn’t exist. The machine-description macro “FRAME_POINTER_REQUIRED” controls whether a target machine supports this flag.

这里,引入了一个”frame pointer”的概念,什么是”stack frame pointer(SFP)”呢?

我们知道,backtrace是利用堆栈中的信息把函数调用关系层层遍历出来的,其中这里的堆栈信息就是SFP。
一般情况下,每一个函数都包含一个堆栈边界指针,也就是说会存在一个栈底和栈顶指针。在X86下,假设堆栈由上往下发展,栈底大地址而栈顶小地址,那么,通常情况下,寄存器ESP为栈顶指针,而EBP就为栈底指针。而EBP和ESP之间的空间就是这个函数的stack frame。
GCC在默认情况下会在每个函数的开始加入一些堆栈设置代码,而在函数退出的时候恢复原来的样子,SFP就是在这个时候设置的。还是看一下这个时候的汇编代码吧 😉

环境:X86+Redhat 9.0,gcc 3.2.2

源文件如下:

$ cat test.c
void a(unsigned long a, unsigned int b)
{
unsigned long i;
unsigned int j;

i = a;
j = b;

i++;

j += 2;

}

默认编译选项:
$ gcc -c test.c -o with_SFP.o

反汇编后是这个样子:
$ objdump -D with_SFP.o

with_SFP.o:     file format elf32-i386

Disassembly of section .text:

00000000 <a>:
0:   55                      push   %ebp
1:   89 e5                   mov    %esp,%ebp
3:   83 ec 08                sub    $0x8,%esp
6:   8b 45 08                mov    0x8(%ebp),%eax
9:   89 45 fc                mov    %eax,0xfffffffc(%ebp)
c:   8b 45 0c                mov    0xc(%ebp),%eax
f:   89 45 f8                mov    %eax,0xfffffff8(%ebp)
12:   8d 45 fc                lea    0xfffffffc(%ebp),%eax
15:   ff 00                   incl   (%eax)
17:   8d 45 f8                lea    0xfffffff8(%ebp),%eax
1a:   83 00 02                addl   $0x2,(%eax)
1d:   c9                      leave
1e:   c3                      ret
Disassembly of section .data:

可以看到函数ENTER时首先把上一层函数的EBP入栈,设置本函数的EBP,然后会根据临时变量的数量和对齐要求去设置ESP,也就产生了函数的stack frame。
我们再看看函数的返回:”leave”指令相当于”mov %ebp,%esp;pop %ebp”,也就是ENTER是两条指令的恢复过程,所以,后面的”ret”指令和”call”指令对应。
这里backtrace就可以根据现有函数EBP指针得知上一个函数的EBP—-栈底再往上保存着上一个函数的EBP和EIP,然后就可以得知函数调用的路径。

SFP是可以在编译时候优化掉的,用”-fomit-frame-pointer”选项

编译:
$ gcc -fomit-frame-pointer -c test.c -o no_SFP.o

$ objdump -D no_SFP.o

no_SFP.o:     file format elf32-i386

Disassembly of section .text:

00000000 <a>:
0:   83 ec 08                sub    $0x8,%esp
3:   8b 44 24 0c             mov    0xc(%esp,1),%eax
7:   89 44 24 04             mov    %eax,0x4(%esp,1)
b:   8b 44 24 10             mov    0x10(%esp,1),%eax
f:   89 04 24                mov    %eax,(%esp,1)
12:   8d 44 24 04             lea    0x4(%esp,1),%eax
16:   ff 00                   incl   (%eax)
18:   89 e0                   mov    %esp,%eax
1a:   83 00 02                addl   $0x2,(%eax)
1d:   83 c4 08                add    $0x8,%esp
20:   c3                      ret
Disassembly of section .data:

 这样代码执行长度缩短了,不使用ebp寄存器,应该能引起效率的提升。 但缺点也很明显,代码易读性变差,最重要的是不能用backtrace调试了。
这么说来是因为通用寄存器不够用,要借用ebp,所以必须加这个参数才能编译成功?
另外一个是提示找不到libdvdnav 和libdvdread(通过zypper 下载的so  没用)  ,这个是因为编译目录中没有这个两个。手动下载即可:
svn checkout svn://svn.mplayerhq.hu/dvdnav/trunk/libdvdread/src libdvdread4
svn checkout svn://svn.mplayerhq.hu/dvdnav/trunk/libdvdnav/src libdvdnav
傻瓜一点的直接把他写入configure:

diff --git a/configure b/configure
index ac64334..717f2e1 100755
--- a/configure
+++ b/configure
@@ -58,6 +58,26 @@
     touch ffmpeg/mp_auto_pull
   fi
+  if ! test -d libdvdread4 ; then
+    echo "No libdvdread4 directory, press enter to download with svn or CTRL+C to abort"
+    read tmp
+    if ! svn checkout svn://svn.mplayerhq.hu/dvdnav/trunk/libdvdread/src libdvdread4 ; then
+      rm -rf libdvdread4
+      echo "Failed to svn checkout libdvdread4"
+      exit 1
+    fi
+  fi
+
+  if ! test -d libdvdnav ; then
+    echo "No libdvdnav directory, press enter to download with svn or CTRL+C to abort"
+    read tmp
+    if ! svn checkout svn://svn.mplayerhq.hu/dvdnav/trunk/libdvdnav/src libdvdnav ; then
+      rm -rf libdvdnav
+      echo "Failed to svn checkout libdvdnav"
+      exit 1
+    fi
+  fi
+
 # Prevent locale nonsense from breaking basic text processing utilities
Advertisements

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s

%d 博主赞过: