- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我帮助维护一个简单的命令行工具,diskmanager
,用于监控磁盘性能不佳,主要是由于太多操作/用户同时使用同一磁盘。我的工作涉及维护一个库 libdisksupervisor.so
,它偶尔用于通过以下方式启动磁盘管理器程序以“监督”它:
LD_PRELOAD=/public/libdisksupervisor.so /sbin/diskmanager
我们这样做的原因是库和应用程序的发布时间表非常不同,由于存在交叉 NDA 等原因无法共享源代码。为了让我们的生活更轻松,diskmanager 的维护者
在应用程序中创建了一些 extern
变量,并在库 (libdonothing.so
) 中添加了一些对“虚拟”函数的调用,它们与 >磁盘管理器
。
当调用 int dummy(void)
时(通常在 libdonothing.so
中找到,但我们通过 LD_PRELOAD
拦截它)'查看 libdisksupervisor.so
,它也包含相同的函数原型(prototype)),我们知道 diskmanager
处于我们可以安全读取 extern int internalStatus
(位于 diskimager
)来 self 们自己的库。 dummy()
的代码非常简单:
# In source for diskmanager
int internalStatus = (-1);
# In libdummy.so
int dummy(void) { return 0; }
# In libdisksupervisor.so
extern int internalStatus;
int dummy(void) { syslog(LOG_ERR, "State:%d", internalStatus);
到目前为止,还不错。几个月前,diskmanager
的维护者之一做了一些愚蠢的事情,从 diskmanager
中删除了 int internalStatus
导致我们的库导致段错误在执行 LD_PRELOAD=/public/libdisksupervisor.so diskmanager
时。当一名初级工程师摸索着 GCC 隐藏属性并将某些值更改为 static
并再次导致段错误时,出现了类似的问题。
有没有办法,在我们的 libdisksupervisor.so
代码中,我们可以在继续之前测试这些 extern
变量的存在(从我们的库的角度来看) ,可能是通过一些神秘的链接器或 GCC 魔法?我知道我可以将 nm
或 objdump
作为预验证脚本的一部分扔给它,但我们需要在 c 中完成此操作单独的图书馆。
谢谢。
最佳答案
Is there any way, within our code in libdisksupervisor.so, we can test for the presence of these extern (from the perspective of our library) variables before proceeding, possibly via some cryptic linker or GCC magic?
你这里有一个时间问题。事实上,您不需要做任何特殊的事情来在编译时测试这些符号的存在和可见性,在您链接的 diskmanager
版本中反对。当您尝试将 libdisksupervisor.so
与运行时不兼容的 diskmanager
版本一起使用时,就会出现此问题。
I know I could just throw nm or objdump at it as part of a pre-validation script, but we need to accomplish this within our c library alone.
我不知道有什么方法可以与您运行程序的方式一起使用,并且不会轻易被 diskmanager
维护意外地挫败。
但也许有一种方法涉及更改您运行程序的方式。如果您目前调用的 libdisksupervisor.so
提供了一个程序入口点(即 main()
)并且您直接运行它,它可以 dlopen()
diskmanager
并通过 dlsym()
检查是否存在所需的符号。然后它可以将控制转移到 diskmanager
的 main()
(也可以通过 dlsym()
访问)。您可以将其视为在系统的动态链接器和 diskmanager
之间插入填充程序。
好消息是我有一个概念验证证明它是可以做到的(见下文)。坏消息是,使主要可执行文件作为共享库加载需要特殊的构建选项,而且让另一方使用此类选项进行构建听起来可能很麻烦。另一方面,这种方法允许他们精确地控制和记录哪些符号暴露在你身边,也许这可以作为一个合适的胡萝卜。
总之,POC由三个C源文件、两个辅助文件和一个Makefile组成:
int dummy(void) {
return 0;
}
#include <stdio.h>
int dummy(void);
#ifndef BREAKME
int internalStatus = 42;
#endif
int main(int argc, char *argv[]) {
printf("dummy() returns %d\n", dummy());
return 0;
}
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
#include <assert.h>
#define TARGET_PATH "./mainprog"
#define NOT_FOUND_STATUS 127
#define MISSING_SYM_STATUS 126
typedef int (*main_type)(int, char **);
static int *internalStatus_p;
#define internalStatus (*internalStatus_p);
int dummy(void) {
return internalStatus;
}
#define LOAD_SYM(dso, name, var) do { \
char *e_; \
var = dlsym(dso, name); \
e_ = dlerror(); \
if (e_) { \
fprintf(stderr, "%s\n", e_); \
return MISSING_SYM_STATUS; \
} \
} while (0)
int main(int argc, char *argv[]) {
void *diskmanager_bin = dlopen(TARGET_PATH, RTLD_LAZY | RTLD_GLOBAL);
char *error;
main_type main_p;
if (!diskmanager_bin) {
fprintf(stderr, "Could not load " TARGET_PATH ": %s\naborting\n", dlerror());
return NOT_FOUND_STATUS;
} else {
error = dlerror();
assert(!error);
}
LOAD_SYM(diskmanager_bin, "internalStatus", internalStatus_p);
LOAD_SYM(diskmanager_bin, "main", main_p);
return main_p(argc, argv);
}
#undef LOAD_SYM
{
main; internalStatus;
};
{
dummy;
};
# sources contributing to a shared library must be built with -fpic or -fPIC
CFLAGS = -fPIC -std=c99
LDFLAGS =
SHLIB_LDFLAGS = -shared
SHLIB_EXTRALIBS = -lc
# Sources contributing to the main program should be built with -fpie or -fPIE
SHMAIN_CFLAGS = -fpie
# The main program must be linked with -pie
SHMAIN_LDFLAGS = -pie
DL_EXTRALIBS = -ldl
LIBDUMMY_SO_VER = 0
LIBDUMMY = libdummy.so.$(LIBDUMMY_SO_VER)
all: mainprog shim
mainprog: main.o $(LIBDUMMY) mainprog_dynamic
$(CC) $(CFLAGS) $(SHMAIN_CFLAGS) $(LDFLAGS) $(SHMAIN_LDFLAGS) -Wl,--dynamic-list=mainprog_dynamic -o $@ $< $(LIBDUMMY) $(SHLIB_EXTRALIBS)
main.o: main.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(SHMAIN_CFLAGS) -c -o $@ $<
libdummy.so.$(LIBDUMMY_SO_VER): libdummy.so
ln -sf $< $@
libdummy.so: dummy.o
$(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(SHLIB_LDFLAGS) -Wl,-soname,libdummy.so.$(LIBDUMMY_SO_VER) $^ $(SHLIB_EXTRALIBS)
shim: shim.o shim_dynamic
$(CC) $(CFLAGS) $(LDFLAGS) -Wl,--dynamic-list=shim_dynamic -o $@ $< $(DL_EXTRALIBS)
test: all
@echo "LD_LIBRARY_PATH=`pwd` ./mainprog :"
@LD_LIBRARY_PATH=`pwd` ./mainprog
@echo "LD_LIBRARY_PATH=`pwd` ./shim :"
@LD_LIBRARY_PATH=`pwd` ./shim
clean:
rm -f *.o *.so *.so.* mainprog shim
这模拟了您描述的情况,您要覆盖的函数驻留在单独的共享库中。它采用 GNU 工具链。成功构建示例(make all
)后,您可以make test
进行演示:
$ make test
LD_LIBRARY_PATH=/tmp/dl ./mainprog :
dummy() returns 0
LD_LIBRARY_PATH=/tmp/dl ./shim :
dummy() returns 42
*_dynamic
文件告诉链接器两个可执行文件中的符号应该包含在导出的(动态)符号中,即使链接中没有引用它们也是如此。
这种方法不允许 shim 直接引用主程序的 internalStatus
变量,因为这样 shim 就需要将主程序链接为一个库,它会被shim 运行时的动态链接器。对变量的引用总是立即绑定(bind),因此如果 internalStatus
消失,超出 shim 的控制,这将导致动态链接器出错。
关于C - Externs - 在 LD_PRELOAD'ed 库中监控值的安全方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39793363/
我的问题:非常具体。我正在尝试想出解析以下文本的最简单方法: ^^domain=domain_value^^version=version_value^^account_type=account_ty
好吧,这就是我的困境: 我正在为 Reddit 子版 block 开发常见问题解答机器人。我在 bool 逻辑方面遇到了麻烦,需要一双更有经验的眼睛(这是我在 Python 中的第一次冒险)。现在,该
它首先遍历所有 y 值,然后遍历所有 x 值。我需要 X 和 y 同时改变。 For x = 3 To lr + 1 For y = 2 To lr anyl.Cells(x, 1)
假设我有一个包含 2 列的 Excel 表格:单元格 A1 到 A10 中的日期和 B1 到 B10 中的值。 我想对五月日期的所有值求和。我有3种可能性: {=SUM((MONTH(A1:A10)=
如何转换 Z-score来自 Z-distribution (standard normal distribution, Gaussian distribution)到 p-value ?我还没有找到
我正在重写一些 Javascript 代码以在 Excel VBA 中工作。由于在这个网站上搜索,我已经设法翻译了几乎所有的 Javascript 代码!但是,有些代码我无法准确理解它在做什么。这是一
我遇到过包含日期格式的时间戳日期的情况。然后我想构建一个图表,显示“点击”项目的数量“每天”, //array declaration $array1 = array("Date" => 0); $a
我是scala的新手! 我的问题是,是否有包含成员的案例类 myItem:Option[String] 当我构造类时,我需要将字符串内容包装在: Option("some string") 要么 So
我正在用 PHP 创建一个登录系统。我需要用户使用他或她的用户名或电子邮件或电话号码登录然后使用密码。因为我知道在 Java 中我们会像 email==user^ username == user 这
我在 C++ 项目上使用 sqlite,但是当我在具有文本值的列上使用 WHERE 时出现问题 我创建了一个 sqlite 数据库: CREATE TABLE User( id INTEGER
当构造函数是显式时,它不用于隐式转换。在给定的代码片段中,构造函数被标记为 explicit。那为什么在 foo obj1(10.25); 情况下它可以工作,而在 foo obj2=10.25; 情况
我知道这是一个主观问题,所以如果需要关闭它,我深表歉意,但我觉得它经常出现,让我想知道是否普遍偏爱一种形式而不是另一种形式。 显然,最好的答案是“重构代码,这样你就不需要测试是否存在错误”,但有时没有
这两个 jQuery 选择器有什么区别? 以下是来自 w3schools.com 的定义: [attribute~=value] 选择器选择带有特定属性,其值包含特定字符串。 [attribute*=
为什么我们需要CSS [attribute|=value] Selector根本当 CSS3 [attribute*=value] Selector基本上完成相同的事情,浏览器兼容性几乎相似?是否存在
我正在解决 regx 问题。我已经有一个像这样的 regx [0-9]*([.][0-9]{2})。这是 amont 格式验证。现在,通过此验证,我想包括不应提供 0 金额。比如 10 是有效的,但
我正在研究计算机科学 A 考试的样题,但无法弄清楚为什么以下问题的正确答案是正确的。 考虑以下方法。 public static void mystery(List nums) { for (
好的,我正在编写一个 Perl 程序,它有一个我收集的值的哈希值(完全在一个完全独立的程序中)并提供给这个 Perl 脚本。这个散列是 (string,string) 的散列。 我想通过 3 种方式对
我有一个表数据如下,来自不同的表。仅当第三列具有值“债务”并且第一列(日期)具有最大值时,我才想从第四列中获取最大值。最终值基于 MAX(DATE) 而不是 MAX(PRICE)。所以用简单的语言来说
我有一个奇怪的情况,只有错误状态保存到数据库中。当“状态”应该为 true 时,我的查询仍然执行 false。 我有具有此功能的 Controller public function change_a
我有一个交易表(针对所需列进行了简化): id client_id value 1 1 200 2 2 150 3 1
我是一名优秀的程序员,十分优秀!