草庐IT

关于linux:加载可执行文件或执行库

codeneng 2023-03-28 原文

Loading executable or executing a library

关于如何执行库或动态加载可执行文件有大量关于 SO 的问题。据我所知,所有答案都归结为:将您的可执行文件编译为与位置无关的代码并使用 dlopen 加载它。这很好用——并且在 macOS 上仍然很好用——直到最近 glibc 发生了变化,它明确禁用了 dlopening PIE。例如,此更改现在在 ArchLinux 上的当前 glibc (2.30) 版本中,并且尝试 dlopen 位置无关的可执行文件会给出错误:"无法动态加载与位置无关的可执行文件"。

很难猜测是什么促成了如此彻底的改变,破坏了如此多的代码和有用的用例。 (关于 Patchwork 和 Bugzilla 的解释对我来说没有多大意义。)但是现在有一个问题:如果您想创建一个也是动态库的可执行文件,或者反之亦然,该怎么办?

其中一条评论链接了一个解决方案。在这里复制它以供后代使用:

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

const char service_interp[] __attribute__((section(".interp"))) ="/lib/ld-linux-x86-64.so.2";

extern"C" {

void lib_entry(void)
{
  printf("Entry point of the service library\
");    
  _exit(0);
}

}

g++ -shared test-no-pie.cpp -o test-no-pie -Wl,-e,lib_entry 编译会生成一个共享对象(动态库),它也可以在 Linux 上执行。

我有两个问题:

  • 如果我想传递命令行参数怎么办?如何修改此解决方案以使其接受 arc,argv
  • 还有其他选择吗?
    • 如何更改解释器路径并将命令行参数传递给 Linux 上的"可执行"共享库的可能重复项?


    请看这个答案:

    https://stackoverflow.com/a/68339111/14760867

    argc, argv 问题在那里没有得到解答,但是当我发现我需要一个时,我一起破解了一些东西以在运行时解析 /proc/self/cmdline 以供 pam_cap.so 使用。


    It's difficult to guess what prompted such a radical change

    并非如此:它从未正常工作过。

    that breaks so much code

    该代码已经以微妙的方式被破坏了。现在您可以清楚地看到它不起作用。

    Are there other alternatives?

    不要那样做吗?

    dlopen执行一个可执行文件解决了什么问题?

    如果这是一个真正的问题,请打开 GLIBC bugzilla 功能请求,解释该问题并请求支持的机制以达到预期的结果。

    更新:

    at least say why"it never worked correctly". Is it some triviality like potentially clashing globals between the executables, or something real?

    线程局部变量是一个不能正常工作的例子。你是否认为它们是"真实的"我不知道。

    代码如下:

    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
    // foo.c
    #include <stdio.h>

    __thread int var;

    __attribute__((constructor))
    static void init()
    {
      var = 42;
      printf("foo.c init: %d %p\
    ", var, &var);
    }

    int bar() {
      printf("foo.c bar:  %d %p\
    ", var, &var);
      return var;
    }

    int main()
    {
      printf("foo.c main: %d %p bar()=%d\
    ", var, &var, bar());
      return 0;
    }
    1
    2
    3
    4
    gcc -g foo.c -o foo -Wl,-E -fpie -pie && ./foo
    foo.c init: 42 0x7fb5dfd7d4fc
    foo.c bar:  42 0x7fb5dfd7d4fc
    foo.c main: 42 0x7fb5dfd7d4fc bar()=42
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // main.c
    // Error checking omitted for brevity
    #include <dlfcn.h>
    #include <stdio.h>

    int main()
    {
      void *h1 = dlopen("./foo", RTLD_LOCAL|RTLD_LAZY);
      int (*bar)(void) = dlsym(h1,"bar");

      printf("main.c: %d\
    ", bar());
      return 0;
    }
    1
    2
    3
    4
    gcc -g main.c -ldl && ./a.out
    foo.c init: 42 0x7fb7305da73c
    foo.c bar:  0 0x7fb7305da73c    <<< what?
    main.c: 0                       <<< what?

    这是使用 GNU C Library (Debian GLIBC 2.28-10) stable release version 2.28.

    底线:这从未被设计为有效,而且您只是碰巧没有踩到许多地雷,所以您认为它有效,而实际上您正在执行未定义的行为。

    • 谢谢你的例子。所以全局变量是问题所在。似乎仍然像把婴儿和洗澡水一起扔出去。另外,如果我将 foo.c 编译为共享库,这是什么原因?即,这两种情况有何不同?
    • 我同意...我们使用可执行共享库是为了方便共享库,该共享库可以通过与 GDB 独立运行来进行调试。 (而不是编译另一个可执行文件 dlopen 并运行 main)但是,我们看到了许多微妙的问题,包括全局变量......
    • 如果示例代码是用 -fPIC 编译的(如本问题所示),则示例代码将正常工作。因此,它不能证明任何事情,并且在拿出更多证据之前,此答案中的所有内容都应仅视为意见和毫无根据的主张。
    • @foxcub 全局变量不是问题。

    有关关于linux:加载可执行文件或执行库的更多相关文章

    1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

      我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

    2. ruby - 其他文件中的 Rake 任务 - 2

      我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

    3. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

      我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

    4. ruby-on-rails - Rails 3 中的多个路由文件 - 2

      Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

    5. ruby-openid:执行发现时未设置@socket - 2

      我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass

    6. ruby - 将差异补丁应用于字符串/文件 - 2

      对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

    7. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

      我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

    8. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

      使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

    9. ruby - 如何在续集中重新加载表模式? - 2

      鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

    10. Ruby 写入和读取对象到文件 - 2

      好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

    随机推荐