草庐IT

关于 c:intel 处理器上的未对齐访问存储

codeneng 2023-03-28 原文

unaligned access store on intel processor

考虑下面的示例。它在标记行出现 gcc 5.4 的段错误时
我用 g++ -O3 -std=c++11 编译它。它在指令 movaps 处失败,我怀疑它执行未对齐的内存访问。可能是 gcc 为这样一个简单的示例生成了非法代码,还是我遗漏了一些东西?
我在 Intel i5-5200U 上运行它。

#include <vector>
#include <memory>
#include <cstdint>

using namespace std;

__attribute__ ((noinline))
void SerializeTo(const vector<uint64_t>& v, uint8_t* dest) {
  for (size_t i = 0; i < v.size(); ++i) {
    *reinterpret_cast<uint64_t*>(dest) = v[i];  // Segfaults here.
    dest += sizeof(uint64_t);
  }
}

int main() {
 std::vector<uint64_t> d(64);

 unique_ptr<uint8_t[]> tmp(new uint8_t[1024]);

 SerializeTo(d, tmp.get() + 6);

 return 0;
}

  • 您可以使用 g++ -S -O2 -fverbose-asm 查看生成的代码
  • 使用 -O2 它不会生成矢量化代码。
  • 然后将 -O2 替换为您想要的任何优化,例如-O3 -march=native
  • 然后它会出现段错误。我不跟随
  • 阅读有关未定义行为的更多信息,尤其是 Lattner 的博客。并且更加信任编译器:它比您的代码经过更多测试。请首先责怪您的代码,而不是编译器。所以确实,你错过了一些东西。也尝试其他编译器(例如 Clang...)和版本。启用所有警告(使用 -Wall -Wextra)
  • 非常感谢@BasileStarynkevitch。我也相信更多的编译器。这就是为什么我在这里寻求建议的原因。你的不是很有帮助:)


您在数组中步进了 6 个字节,所以它现在未对齐。编译器无法知道它必须避免需要对齐的指令;这就是类型双关语是未定义行为的原因。

  • 是的,它没有对齐。我认为如果不能确定地址是否对齐,编译器应该生成可以处理未对齐地址的代码。你不同意吗?
  • 编译器"知道"地址是对齐的,因为所有使此类指针的有效方法都会产生对齐的地址。 (这里,"知道"意味着"可以假设,因为替代方案是结果无关紧要的未定义行为"。)
  • @Roman:即使数据已对齐,当您违反严格别名时,编译器也没有义务生成有效代码。 (这是未定义的行为)
  • 我不同意。上次我检查时,我可以写入属于我的进程的任意内存位置。 SerializeTo 也没有内联,因此它不能假设 dest 是对齐的。想象一下 memcpy 会突然决定它的参数应该对齐。这是胡说八道
  • @AndyG 我怎么会违反严格的别名?我认为强制转换为 uint8_t 是有效的?
  • @Roman:内核和处理器将允许您使用对其有效的指令写入任何内存位置。但是你的牛肉是编译器(又名 C ),而不是操作系统。 C 说你不能这样做,现在你知道为什么了。 memcpy 需要 void*,所以它当然不知道有什么对齐要求。
  • @DavisHerring我不明白这是什么意思"C说你不能这样做,现在你知道为什么了。"。并且用 "void*" 替换 dest 的类型当然不能解决它
  • Roman:如果您想了解更多信息,请参阅标准中的部分(例如,您可以免费找到的 N4296 草案)下 [basic.lval]通过以下类型之一以外的泛左值的对象行为未定义"
  • @Roman:C 不是汇编程序。编译器非常有用地使用高性能指令,而您要求的不仅仅是 "-O3 please"。这样做的代价是你必须遵守语言的规则,否则坏事就会发生,正如你所见。我没有说"使用 void*";我说 memcpy 有更宽松的要求是有原因的。
  • @AndyG 谢谢,你是对的。我想如果其中一种类型是 (unsigned) char 那么双向转换是安全的。显然只有 1 个方向是有效的。


在 c 中合法地执行类型双关的方法很少。

魔术函数 std::memcpy 是这里选择的工具:

__attribute__ ((noinline))
void SerializeTo(const vector<uint64_t>& v, uint8_t* dest) {
  for (size_t i = 0; i < v.size(); ++i) {
      std::memcpy(dest, std::addressof(v[i]), sizeof(v[i]));
    dest += sizeof(uint64_t);
  }
}

结果输出与 -std=c++11 -O3 -march=native -Wall -pedantic

SerializeTo(std::vector<unsigned long, std::allocator<unsigned long> > const&, unsigned char*):   # @SerializeTo(std::vector<unsigned long, std::allocator<unsigned long> > const&, unsigned char*)
        mov     rax, qword ptr [rdi]
        cmp     qword ptr [rdi + 8], rax
        je      .LBB0_3
        xor     ecx, ecx
.LBB0_2:                                # =>This Inner Loop Header: Depth=1
        mov     rax, qword ptr [rax + 8*rcx]
        mov     qword ptr [rsi + 8*rcx], rax
        add     rcx, 1
        mov     rax, qword ptr [rdi]
        mov     rdx, qword ptr [rdi + 8]
        sub     rdx, rax
        sar     rdx, 3
        cmp     rcx, rdx
        jb      .LBB0_2
.LBB0_3:
        ret

https://godbolt.org/g/ReGA9N

  • 谢谢理查兹。那么下面的解决方案呢? void SerializeTo(const vector<uint64_t>

有关关于 c:intel 处理器上的未对齐访问存储的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  4. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  5. ruby - 续集在添加关联时访问many_to_many连接表 - 2

    我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以

  6. ruby-on-rails - date_field_tag,如何设置默认日期? [ rails 上的 ruby ] - 2

    我想设置一个默认日期,例如实际日期,我该如何设置?还有如何在组合框中设置默认值顺便问一下,date_field_tag和date_field之间有什么区别? 最佳答案 试试这个:将默认日期作为第二个参数传递。youcorrectlysetthedefaultvalueofcomboboxasshowninyourquestion. 关于ruby-on-rails-date_field_tag,如何设置默认日期?[rails上的ruby],我们在StackOverflow上找到一个类似的问

  7. ruby-on-rails - openshift 上的 rails 控制台 - 2

    我将我的Rails应用程序部署到OpenShift,它运行良好,但我无法在生产服务器上运行“Rails控制台”。它给了我这个错误。我该如何解决这个问题?我尝试更新ruby​​gems,但它也给出了权限被拒绝的错误,我也无法做到。railsc错误:Warning:You'reusingRubygems1.8.24withSpring.UpgradetoatleastRubygems2.1.0andrun`gempristine--all`forbetterstartupperformance./opt/rh/ruby193/root/usr/share/rubygems/rubygems

  8. ruby-on-rails - 相关表上的范围为 "WHERE ... LIKE" - 2

    我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que

  9. ruby - Rack:如何将 URL 存储为变量? - 2

    我正在编写一个简单的静态Rack应用程序。查看下面的config.ru代码:useRack::Static,:urls=>["/elements","/img","/pages","/users","/css","/js"],:root=>"archive"map'/'dorunProc.new{|env|[200,{'Content-Type'=>'text/html','Cache-Control'=>'public,max-age=6400'},File.open('archive/splash.html',File::RDONLY)]}endmap'/pages/search.

  10. ruby-on-rails - Ruby - 如何从 ruby​​ 上的 .pfx 文件中提取公钥、rsa 私钥和 CA key - 2

    我有一个.pfx格式的证书,我需要使用ruby​​提取公共(public)、私有(private)和CA证书。使用shell我可以这样做:#ExtractPublicKey(askforpassword)opensslpkcs12-infile.pfx-outfile_public.pem-clcerts-nokeys#ExtractCertificateAuthorityKey(askforpassword)opensslpkcs12-infile.pfx-outfile_ca.pem-cacerts-nokeys#ExtractPrivateKey(askforpassword)o

随机推荐