我有几个问题:
a.out取代ELF?有了汇编指令和C程序的一些背景知识,我可以看到编译函数的样子,但有趣的是我从来没有仔细考虑过编译后的C++类是什么样的.
bash$ cat class.cpp
#include<iostream>
class Base
{
int i;
float f;
};
bash$ g++ -c class.cpp
Run Code Online (Sandbox Code Playgroud)
我跑了:
bash$objdump -d class.o
bash$readelf -a class.o
Run Code Online (Sandbox Code Playgroud)
但是我得到的东西很难理解.
有人可以解释一下或提出一些好的起点.
我有一个linux C程序来处理发送到TCP套接字(绑定到特定端口)的请求.我希望能够通过对该端口的请求来查询C程序的内部状态,但我不想硬编码可以查询的全局变量.因此,我希望查询包含全局字符串名称和C代码,以在符号表中查找该字符串以查找其地址,然后通过TCP套接字将其值发回.当然,符号表不得被剥离.那么C程序甚至可以找到自己的符号表,是否有一个库接口用于查找给出其名称的符号?这是一个用gcc构建的ELF可执行C程序.
g ++编译器具有零成本异常处理功能.据我所知,try什么都不做,但是当抛出异常时,会执行异常处理程序的子例程.像这样:
void foo() {
try {
bar(); // throws.
} catch (Type exc) {
baz();
}
}
Run Code Online (Sandbox Code Playgroud)
伪代码(c-stylish)看起来像这样:
void foo() {
bar();
return;
catch1_Type:
baz();
}
Run Code Online (Sandbox Code Playgroud)
bar()抛出.例程例程执行以下操作:
啊,返回地址是函数foo()!并且返回地址在第一个try-catch块中,我们抛出类型Type,因此异常处理程序例程位于地址foo + catch1_Type.所以清理堆栈让我们最终到达那里!
现在我的问题是:有没有办法在C中实现它?(可以是C99或更新,虽然我对gcc支持的C语言感兴趣).我知道我可以使用例如libunwind进行堆栈检查和遍历,虽然我不知道如何获取catch1_Type标签的地址.这可能是不可能的.
异常处理程序可能是一个不同的函数,同样可以,但是如何foo在另一个函数中获取stackframe的局部变量的地址?这似乎也是不可能的.
所以...有什么办法吗?我不想用这个进入汇编程序,但如果其他一切都失败也是可以接受的(尽管局部变量 - 伙计,如果使用不同的优化级别,你永远不会知道它们在哪里).
并且要明确 - 这个问题的目的是避免使用 setjmp/longjmp方法.
编辑:我发现了一个非常酷的想法,但完全不起作用:
gcc中的嵌套函数.他们能做什么?
下行阻止我做任何零成本的事情:
我目前正在C中执行自己的objdump实现.
对于我的-s选项,我必须显示ELF文件部分的全部内容.
我做得很好,但我显示的部分比"真正的"objdump更多.
实际上,它不会输出.bss,.shstrtab,.symtab和.strtab部分.
我正在环顾Shdr结构上的sh_flags值,但我找不到任何逻辑......
为什么objdump -s没有显示这些部分?
我试图检索ELF二进制文件中的附加部分的内容.此时,我使用以下代码检索每个部分的名称:
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#pragma pack(push,1)
#pragma pack(pop)
#define EI_NIDENT 16
/* 32-bit ELF base types. */
typedef unsigned int Elf32_Addr;
typedef unsigned short Elf32_Half;
typedef unsigned int Elf32_Off;
typedef signed int Elf32_Sword;
typedef unsigned int Elf32_Word;
/* 64-bit ELF base types. */
typedef unsigned long long Elf64_Addr;
typedef unsigned short Elf64_Half;
typedef signed short Elf64_SHalf;
typedef unsigned long long Elf64_Off;
typedef signed int Elf64_Sword;
typedef unsigned int Elf64_Word;
typedef unsigned long long Elf64_Xword;
typedef …Run Code Online (Sandbox Code Playgroud) 我正在Ubuntu 14.04(64位)编译深度学习库Caffe.
Version: 2.4.8+dfsg1-2ubuntu1从ubuntu软件包服务器安装OpenCV():
sudo apt-get install libopencv-dev
Caffe使用CMake 2.8 编译.
链接错误:
链接CXX可执行文件
/usr/lib/x86_64-linux-gnu/libopencv_highgui.so.2.4.8:未定义引用`TIFFOpen@LIBTIFF_4.0'
似乎找不到TIFF库的一些符号.我努力找到原因(没有运气).这是关于库的一些信息.
TIFF库链接 libopencv_highgui.so.2.4.8
$ ldd libopencv_highgui.so.2.4.8 | grep tiff
libtiff.so.5 => /usr/lib/x86_64-linux-gnu/libtiff.so.5(0x00007f978313b000)
导入符号 libopencv_highgui.so.2.4.8
$ readelf -s libopencv_highgui.so.2.4.8 | grep TIFFOpen
62:0000000000000000 0 FUNC GLOBAL DEFAULT UND TIFFOpen@LIBTIFF_4.0(9)
注意:@符号名称中只有一个.
$ nm -D libopencv_highgui.so.2.4.8 | grep TIFFOpen
你TIFFOpen
出口符号libtiff.so.5:
$ nm -D /usr/lib/x86_64-linux-gnu/libtiff.so.5
0000000000000000 A LIBTIFF_4.0
...
00000000000429f0 T TIFFOpen
...
$ readelf -s /usr/lib/x86_64-linux-gnu/libtiff.so.5|grep TIFFOpen …
我将从最终的问题开始:在C中使用gcc,是否可以获得__func__(或等效地__FUNCTION__)存储在除.rodata(或其中任何-mrodata=点)或子部分之外的部分的值(?)?
完整的解释:
假设我有一个记录宏:
#define LOG(fmt, ...) log_internal(__FILE__, __LINE__, __func__, fmt, ##__VA_ARGS__)
Run Code Online (Sandbox Code Playgroud)
(##当且仅当__VA_ARGS__列表为空时,在该一元上下文中使用的字符串连接运算符使用前面的逗号,从而允许使用带或不带参数的格式字符串.)
然后我可以正常使用宏:
void my_function(void) {
LOG("foo!");
LOG("bar: %p", &bar);
}
Run Code Online (Sandbox Code Playgroud)
可能打印(显然取决于实施log_internal):
foo.c:201(my_function) foo!
foo.c:202(my_function) bar: 0x12345678
Run Code Online (Sandbox Code Playgroud)
在这种情况下,格式字符串("foo"和"bar: %p")和预处理程序字符串("foo.c"和"my_function")是匿名只读数据,它们会.rodata自动放入该部分.
但是我说他希望他们去另一个地方(我在一个嵌入式平台上运行几乎所有来自RAM的速度,但内存限制正在推动将一些东西转移到ROM中).移动__FILE__和格式字符串"简单" :
#define ROM_STR(str) (__extension__({static const __attribute__((__section__(".rom_data"))) char __c[] = (str); (const char *)&__c;}))
#define LOG(fmt, ...) log_internal(ROM_STR(__FILE__), __LINE__, __func__, ROM_STR(fmt), ##__VA_ARGS__)
Run Code Online (Sandbox Code Playgroud)
你不能放一个__attribute__匿名字符串,所以 …
我从http://newlib.sourcearchive.com/documentation/1.18.0/init_8c-source.html查看了__libc_init_array的源代码.
但我不太明白这个功能是做什么的.
我知道这些符号
/* These magic symbols are provided by the linker. */
extern void (*__preinit_array_start []) (void) __attribute__((weak));
extern void (*__preinit_array_end []) (void) __attribute__((weak));
extern void (*__init_array_start []) (void) __attribute__((weak));
extern void (*__init_array_end []) (void) __attribute__((weak));
extern void (*__fini_array_start []) (void) __attribute__((weak));
extern void (*__fini_array_end []) (void) __attribute__((weak));
Run Code Online (Sandbox Code Playgroud)
在链接描述文件中定义.
链接器脚本的一部分可能如下所示:
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} >FLASH
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN …Run Code Online (Sandbox Code Playgroud) 据我所知,在典型的ELF二进制文件中,函数通过过程链接表(PLT)调用.函数的PLT条目通常包含跳转到全局偏移表(GOT)条目.此条目将首先引用一些代码将实际的函数地址加载到GOT中,并在第一次调用(lazy binding)后包含实际的函数地址.
确切地说,在延迟绑定之前,GOT入口指向PLT,跳转到GOT之后的指令.这些指令通常会跳转到PLT的头部,从那里调用一些绑定例程,然后更新GOT条目.
现在我想知道为什么有两个间接(调用PLT然后从GOT跳转到一个地址),而不是仅仅保留PLT并直接从GOT调用地址.看起来这可以节省跳跃和完整的PLT.您当然仍需要一些调用绑定例程的代码,但这可能在PLT之外.
有什么我想念的吗?额外PLT的目的是什么?
更新: 正如评论中所建议的那样,我创建了一些(伪)代码ASCII艺术来进一步解释我所指的内容:
据我所知,在懒惰绑定之前的当前PLT方案中就是这种情况:( PLT之间的一些间接printf由"......"表示.)
Program PLT printf
+---------------+ +------------------+ +-----+
| ... | | push [0x603008] |<---+ +-->| ... |
| call j_printf |--+ | jmp [0x603010] |----+--...--+ +-----+
| ... | | | ... | |
+---------------+ +-->| jmp [printf@GOT] |-+ |
| push 0xf |<+ |
| jmp 0x400da0 |----+
| ... |
+------------------+
Run Code Online (Sandbox Code Playgroud)
...和懒惰绑定后:
Program PLT printf
+---------------+ +------------------+ +-----+
| ... | | push [0x603008] | +-->| ... | …Run Code Online (Sandbox Code Playgroud)