嵌套共享库中的系统功能超载
简单的方案
- 应用程序使用
WRITE
来自libc
的函数,并链接到共享库。 - 共享库定义
写
函数。 - 原始
Write
libc
的函数将由其版本从共享库中重载。
嵌套方案
- 应用程序使用
WRITE
功能从libc
。 - 共享库1并未定义其自己的
写
,但取决于共享库2。 - 共享库2定义
write
函数。 - 编号
写入
函数将不是被第二个共享库中的版本替换。
我想明白为什么?如何使其与嵌套的共享库依赖关系一起使用。
这是确切的代码示例:
main.c
#include <unistd.h>
int main() {
write(1,"Hello\n",6);
return 0;
}
shared-lib-1.c
#include <unistd.h>
__attribute__((constructor))
void shared_lib_1_constructor(void) {
char str[] = "shared-lib-1-constructor\n";
write(1, str, sizeof(str));
}
共享lib-2.c
#include <stdlib.h>
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count) {
exit(1);
return 0;
}
__attribute__((constructor))
void shared_lib_2_constructor(void) {
char str[] = "shared-lib-2-constructor\n";
write(1, str, sizeof(str));
}
build.sh
#!/usr/bin/env sh
set -v
set -e
gcc -g -fPIC -shared shared-lib-2.c -o libshared-lib-2.so
gcc -g -fPIC -shared -Wl,-rpath . -L`pwd` -lshared-lib-2 shared-lib-1.c -o libshared-lib-1.so
gcc -g -L`pwd` -Wl,-rpath . -lshared-lib-1 main.c
a.out
给出:
[smirnov@localhost tmp]$ ./a.out
shared-lib-2-constructor
shared-lib-1-constructor
Hello
write> write
是不被共享lib-2覆盖。即使从共享LIB-2代码中也完全忽略了它。
它如何与嵌套库一起使用?移动写入
定义为shared-lib-1 做工作,它超载glibc
版本,从应用程序退出。
使用LD_DEBUG = ALL
显示解决方案的运行应用程序:
36441: Initial object scopes
36441: object=./a.out [0]
36441: scope 0: ./a.out ./libshared-lib-1.so /lib64/libc.so.6 ./libshared-lib-2.so /lib64/ld-linux-x86-64.so.2
...
36441: calling init: ./libshared-lib-2.so
36441:
36441: symbol=write; lookup in file=./a.out [0]
36441: symbol=write; lookup in file=./libshared-lib-1.so [0]
36441: symbol=write; lookup in file=/lib64/libc.so.6 [0]
36441: binding file ./libshared-lib-2.so [0] to /lib64/libc.so.6 [0]: normal symbol `write'
为什么libc
放置在共享lib-1和共享lib-2之间? 看来ld
按以下顺序解决:
- A.Out的所有依赖性,但不是递归的,只有第一个级别的
- 所有亚依赖性,但不是recursivelly
- so y ...
我发现的唯一解决方案是要使用
LD_DYNAMIC_WEAK=1 ./a.out
是否有办法预先修复行为?
Simple scenario
- Application uses
write
function fromlibc
and links to shared library. - Shared library defines
write
function. - Original
write
function fromlibc
will be overloaded by its version from the shared library.
Nested scenario
- Application uses
write
function fromlibc
. - Shared library 1 does not defines its own
write
but depends on shared library 2. - Shared library 2 defines
write
function. - No.
write
function will NOT be replaced with a version from the second shared library.
I do want to understand Why so ? How to make it work with nested shared library dependencies.
Here is exact code examples:
main.c
#include <unistd.h>
int main() {
write(1,"Hello\n",6);
return 0;
}
shared-lib-1.c
#include <unistd.h>
__attribute__((constructor))
void shared_lib_1_constructor(void) {
char str[] = "shared-lib-1-constructor\n";
write(1, str, sizeof(str));
}
shared-lib-2.c
#include <stdlib.h>
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count) {
exit(1);
return 0;
}
__attribute__((constructor))
void shared_lib_2_constructor(void) {
char str[] = "shared-lib-2-constructor\n";
write(1, str, sizeof(str));
}
build.sh
#!/usr/bin/env sh
set -v
set -e
gcc -g -fPIC -shared shared-lib-2.c -o libshared-lib-2.so
gcc -g -fPIC -shared -Wl,-rpath . -L`pwd` -lshared-lib-2 shared-lib-1.c -o libshared-lib-1.so
gcc -g -L`pwd` -Wl,-rpath . -lshared-lib-1 main.c
Execution of the a.out
gives:
[smirnov@localhost tmp]$ ./a.out
shared-lib-2-constructor
shared-lib-1-constructor
Hello
write
was not overwritten by shared-lib-2. It was completely ignored even from shared-lib-2 code.
How does it work with nested libraries ? Moving write
definition to shared-lib-1 does work, it overloads glibc
version and exits from application.
Running application with LD_DEBUG=all
shows order of resolving:
36441: Initial object scopes
36441: object=./a.out [0]
36441: scope 0: ./a.out ./libshared-lib-1.so /lib64/libc.so.6 ./libshared-lib-2.so /lib64/ld-linux-x86-64.so.2
...
36441: calling init: ./libshared-lib-2.so
36441:
36441: symbol=write; lookup in file=./a.out [0]
36441: symbol=write; lookup in file=./libshared-lib-1.so [0]
36441: symbol=write; lookup in file=/lib64/libc.so.6 [0]
36441: binding file ./libshared-lib-2.so [0] to /lib64/libc.so.6 [0]: normal symbol `write'
Why libc
placed in between shared-lib-1 and shared-lib-2 ?
It seems that ld
resolves in the following order:
- all dependencies of a.out, but not recursivelly, only first level
- all subdependencies, but not recursivelly
- so on...
The only solution I've found is to use
LD_DYNAMIC_WEAK=1 ./a.out
Is there a way to fix behavior in advance ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这不是我所期望的,我花了一段时间才弄清楚为什么会发生。
问题是
libc.so.6
不定义写
函数(如果这样做,无论依赖关系是否是直接的,一切都将工作相同)。相反,
libc.so.6
定义版本为写@@ glibc_2.2.5
符号。正是符号版本(GNU扩展)会干扰您所需的结果。比较:
执行符号分辨率时,加载程序在搜索范围中查找符号版本(
A.Out
在正面)。使用libshared-lib-1.so
,加载程序发现它应该寻找write> write
版本glibc_2.2.2.5
(只能找到它在libc.so.6
)中,忽略了libshared-lib-2.so
中未反之的定义。如果您可以控制应用程序链接行,则最好使用
a.out
使用libshared-lib-2.so
:如果您无法控制应用程序链接行,您应该能够使用链接器
- 版本 - script
flag和适当的版本脚本提供版本的write@glibc_2.2.5
。但是我最初的尝试失败了,我不确定是否可以使这种方法起作用。This is not what I expected, and it took me a while to figure out why it's happening.
The issue is that
libc.so.6
doesn't definewrite
function (if it did, everything would have worked the same whether the dependency is direct or not).Instead,
libc.so.6
defines a versionedwrite@@GLIBC_2.2.5
symbol. It is that symbol versioning (a GNU extension) which interferes with your desired outcome.Compare:
When performing symbol resolution, the loader looks up symbol version in the search scope (and
a.out
is at the front). Withlibshared-lib-1.so
, the loader finds that it should be looking forwrite
versionGLIBC_2.2.5
(which can only be found inlibc.so.6
), and ignores the un-versioned definition inlibshared-lib-2.so
.If you can control the application link line, it's best to link
a.out
withlibshared-lib-2.so
:If you can't control the application link line, you should be able to provide a versioned
write@GLIBC_2.2.5
using the linker--version-script
flag and appropriate version script. But my initial attempt at doing that failed, and I am not sure this approach can be made to work.